home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 24 / CU Amiga Magazine's Super CD-ROM 24 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-07].iso / CUCD / Utilities / vim-5.1 / src / syntax.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-06  |  140.8 KB  |  5,672 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * syntax.c: code for syntax highlighting
  11.  */
  12.  
  13. #include "vim.h"
  14.  
  15. /*
  16.  * Structure that stores information about a highlight group.
  17.  * The ID of a highlight group is also called group ID.  It is the index in
  18.  * the highlight_ga array PLUS ONE.
  19.  */
  20. struct hl_group
  21. {
  22.     char_u    *sg_name;    /* highlight group name */
  23.     char_u    *sg_name_u;    /* uppercase of sg_name */
  24. /* for normal terminals */
  25.     int        sg_term;    /* "term=" highlighting attributes */
  26.     char_u    *sg_start;    /* terminal string for start highl */
  27.     char_u    *sg_stop;    /* terminal string for stop highl */
  28.     int        sg_term_attr;    /* NextScreen attr for term mode */
  29. /* for color terminals */
  30.     int        sg_cterm;    /* "cterm=" highlighting attr */
  31.     int        sg_cterm_bold;    /* bold attr was set for light color */
  32.     int        sg_cterm_fg;    /* terminal fg color number + 1 */
  33.     int        sg_cterm_bg;    /* terminal bg color number + 1 */
  34.     int        sg_cterm_attr;    /* NextScreen attr for color term mode */
  35. #ifdef USE_GUI
  36. /* for when using the GUI */
  37.     int        sg_gui;        /* "gui=" highlighting attributes */
  38.     GuiColor    sg_gui_fg;    /* GUI foreground color handle + 1 */
  39.     char_u    *sg_gui_fg_name;/* GUI foreground color name */
  40.     GuiColor    sg_gui_bg;    /* GUI background color handle + 1 */
  41.     char_u    *sg_gui_bg_name;/* GUI background color name */
  42.     GuiFont    sg_font;    /* GUI font handle */
  43.     char_u    *sg_font_name;  /* GUI font name */
  44.     int        sg_gui_attr;    /* NextScreen attr for GUI mode */
  45. #endif
  46.     int        sg_link;    /* link to this highlight group ID */
  47.     int        sg_set;        /* combination of SG_* */
  48. };
  49.  
  50. #define SG_TERM        1    /* term has been set */
  51. #define SG_CTERM    2    /* cterm has been set */
  52. #define SG_GUI        4    /* gui has been set */
  53. #define SG_LINK        8    /* link has been set */
  54.  
  55. static struct growarray highlight_ga;        /* highlight groups for
  56.                            'highlight' option */
  57.  
  58. #define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
  59.  
  60. static int include_link = FALSE;    /* include "link" for expansion */
  61.  
  62. /*
  63.  * The "term", "cterm" and "gui" arguments can be any combination of the
  64.  * following names, separated by commas (but no spaces!).
  65.  */
  66. static char *(hl_name_table[]) =
  67.     {"bold", "standout", "underline", "italic", "reverse", "inverse", "NONE"};
  68. static int hl_attr_table[] =
  69.     {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
  70.  
  71. static int get_attr_entry  __ARGS((struct growarray *table, struct attr_entry *aep));
  72. static void highlight_list_one __ARGS((int id));
  73. static int syn_namen2id __ARGS((char_u *linep, int len));
  74. static void syn_unadd_group __ARGS((void));
  75. static void set_hl_attr __ARGS((int idx));
  76. static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
  77. static int syn_add_group __ARGS((char_u *name));
  78. static int syn_list_header __ARGS((int did_header, int outlen, int id));
  79. static void highlight_clear __ARGS((int idx));
  80.  
  81. #ifdef USE_GUI
  82. static void gui_do_one_color __ARGS((int idx));
  83. static int  set_group_colors __ARGS((char_u *name, GuiColor *fgp, GuiColor *bgp));
  84. static GuiColor color_name2handle __ARGS((char_u *name));
  85. static GuiFont font_name2handle __ARGS((char_u *name));
  86. #endif
  87.  
  88. /*
  89.  * An attribute number is the index in attr_table plus ATTR_OFF.
  90.  */
  91. #define ATTR_OFF (HL_ALL + 1)
  92.  
  93. #ifdef SYNTAX_HL
  94.  
  95. #define SYN_NAMELEN    50        /* maximum length of a syntax name */
  96.  
  97. /* different types of offsets that are possible */
  98. #define SPO_MS_OFF    0    /* match  start offset */
  99. #define SPO_ME_OFF    1    /* match  end    offset */
  100. #define SPO_HS_OFF    2    /* highl. start offset */
  101. #define SPO_HE_OFF    3    /* highl. end    offset */
  102. #define SPO_RS_OFF    4    /* region start offset */
  103. #define SPO_RE_OFF    5    /* region end    offset */
  104. #define SPO_LC_OFF    6    /* leading context offset */
  105. #define SPO_COUNT    7
  106.  
  107. static char *(spo_name_tab[SPO_COUNT]) =
  108.         {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
  109.  
  110. /*
  111.  * The patterns that are being searched for are stored in a syn_pattern.
  112.  * A match item consists of one pattern.
  113.  * A start/end item consists of n start patterns and m end patterns.
  114.  * A start/skip/end item consists of n start patterns, one skip pattern and m
  115.  * end patterns.
  116.  * For the latter two, the patterns are always consecutive: start-skip-end.
  117.  *
  118.  * A character offset can be given for the matched text (_m_start and _m_end)
  119.  * and for the actually highlighted text (_h_start and _h_end).
  120.  */
  121. struct syn_pattern
  122. {
  123.     char         sp_type;        /* see SPTYPE_ defines below */
  124.     char         sp_syncing;        /* this item used for syncing */
  125.     short         sp_flags;        /* see HL_ defines below */
  126.     short         sp_syn_id;        /* highlight group ID of item */
  127.     short         sp_syn_match_id;   /* highlight group ID of pattern */
  128.     char_u        *sp_pattern;        /* regexp to match, pattern */
  129.     vim_regexp        *sp_prog;        /* regexp to match, program */
  130.     int             sp_ic;            /* ignore-case flag for sp_prog */
  131.     short         sp_off_flags;        /* see below */
  132.     int             sp_offsets[SPO_COUNT];    /* offsets */
  133.     short        *sp_cont_list;        /* cont. group IDs, if non-zero */
  134.     short        *sp_next_list;        /* next group IDs, if non-zero */
  135.     int             sp_sync_idx;        /* sync item index (syncing only) */
  136.     int             sp_line_id;        /* ID of last line where tried */
  137.     int             sp_startcol;        /* next match in sp_line_id line */
  138. };
  139.  
  140. /* The sp_off_flags are computed like this:
  141.  * offset from the start of the matched text: (1 << SPO_XX_OFF)
  142.  * offset from the end     of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
  143.  * When both are present, only one is used.
  144.  */
  145.  
  146. #define SPTYPE_MATCH    1    /* match keyword with this group ID */
  147. #define SPTYPE_START    2    /* match a regexp, start of item */
  148. #define SPTYPE_END    3    /* match a regexp, end of item */
  149. #define SPTYPE_SKIP    4    /* match a regexp, skip within item */
  150.  
  151. #define HL_CONTAINED    0x01    /* not used on toplevel */
  152. #define HL_TRANSP    0x02    /* has no highlighting    */
  153. #define HL_ONELINE    0x04    /* match within one line only */
  154. #define HL_HAS_EOL    0x08    /* end pattern that matches with $ */
  155. #define HL_SYNC_HERE    0x10    /* sync point after this item (syncing only) */
  156. #define HL_SYNC_THERE    0x20    /* sync point at current line (syncing only) */
  157. #define HL_MATCH    0x40    /* use match ID instead of item ID */
  158. #define HL_SKIPNL    0x80    /* nextgroup can skip newlines */
  159. #define HL_SKIPWHITE    0x100    /* nextgroup can skip white space */
  160. #define HL_SKIPEMPTY    0x200    /* nextgroup can skip empty lines */
  161. #define HL_KEEPEND    0x400    /* end match always kept */
  162.  
  163. #define SYN_ITEMS(buf)    ((struct syn_pattern *)((buf)->b_syn_patterns.ga_data))
  164.  
  165. #define NONE_IDX    -2    /* value of sp_sync_idx for "NONE" */
  166.  
  167. /*
  168.  * Flags for b_syn_sync_flags:
  169.  */
  170. #define SF_CCOMMENT    0x01    /* sync on a C-style comment */
  171. #define SF_MATCH    0x02    /* sync by matching a pattern */
  172.  
  173. /*
  174.  * Struct used to store states for the start of some lines for a buffer.
  175.  */
  176. struct buf_state
  177. {
  178.     int        bs_idx;        /* index of pattern */
  179.     int        bs_flags;        /* flags for pattern */
  180. };
  181.  
  182. #define SYN_STATE_P(ssp)    ((struct buf_state *)((ssp)->ga_data))
  183.  
  184. /*
  185.  * Settings for keyword hash table.  It uses a simplistic hash function: add
  186.  * all characters together, modulo KHASH_SIZE.
  187.  */
  188. #define KHASH_SIZE    512
  189. #define KHASH_MASK    (KHASH_SIZE - 1)
  190. #define MAXKEYWLEN    80        /* maximum length of a keyword */
  191.  
  192. /*
  193.  * The attributes of the syntax item that has been recognized.
  194.  */
  195. static int current_attr = 0;        /* attr of current syntax word */
  196. static int current_id = 0;        /* ID of current char for syn_get_id() */
  197. static int current_trans_id = 0;    /* idem, transparancy removed */
  198.  
  199. /*
  200.  * For the current state we need to remember more than just the idx.
  201.  * When si_m_endcol is 0, the items other than si_idx are unknown.
  202.  */
  203. struct state_item
  204. {
  205.     int        si_idx;            /* index of syntax pattern */
  206.     int        si_id;            /* highlight group ID for keywords */
  207.     int        si_trans_id;        /* idem, transparancy removed */
  208.     int        si_m_lnum;            /* lnum of the match */
  209.     int        si_m_startcol;        /* starting column of the match */
  210.     int        si_m_endcol;        /* ending column of the match */
  211.     int        si_h_startcol;        /* starting column of the highlighting */
  212.     int        si_h_endcol;        /* ending column of the highlighting */
  213.     int        si_eoe_col;            /* ending column of end pattern */
  214.     int        si_end_idx;            /* group ID for end pattern or zero */
  215.     int        si_ends;            /* if match ends after si_m_endcol */
  216.     int        si_attr;            /* attributes in this state */
  217.     int        si_flags;            /* HL_HAS_EOL flag in this state, and
  218.                        HL_SKIP* for si_next_list */
  219.     short   *si_cont_list;        /* list of contained groups */
  220.     short   *si_next_list;        /* nextgroup IDs after this item ends */
  221. };
  222.  
  223. #define KEYWORD_IDX    -1        /* value of si_idx for keywords */
  224. #define CONTAINS_ALLBUT    9999        /* value of id for contains ALLBUT */
  225. #define ID_LIST_ALL    (short *)-1 /* valid of si_cont_list for containing all
  226.                        but contained groups */
  227.  
  228. /*
  229.  * The next possible match for any pattern is remembered, to avoid having to
  230.  * try for a match in each column.
  231.  * If next_match_idx == -1, not tried (in this line) yet.
  232.  * If next_match_col == MAXCOL, no match found in this line.
  233.  */
  234. static int next_match_col;        /* column for start of next match */
  235. static int next_match_m_endcol;        /* column for end of next match */
  236. static int next_match_h_startcol;   /* column for highl. start of next match */
  237. static int next_match_h_endcol;        /* column for highl. end of next match */
  238. static int next_match_idx;        /* index of matched item */
  239. static int next_match_flags;        /* flags for next match */
  240. static int next_match_eos_col;        /* column for end of start pattern */
  241. static int next_match_eoe_col;        /* column for end of end pattern */
  242. static int next_match_end_idx;        /* ID of group for end pattern or zero */
  243.  
  244. /*
  245.  * A state stack is an array of integers or struct state_item, stored in a
  246.  * struct growarray.  A state stack is invalid if it's itemsize entry is zero.
  247.  */
  248. #define INVALID_STATE(ssp)  ((ssp)->ga_itemsize == 0)
  249. #define VALID_STATE(ssp)    ((ssp)->ga_itemsize != 0)
  250.  
  251. /*
  252.  * The current state (within the line) of the recognition engine.
  253.  */
  254. static BUF    *syn_buf;        /* current buffer for highlighting */
  255. static linenr_t current_lnum = 0;    /* lnum of current state */
  256. static int    current_state_stored = 0; /* TRUE if stored current state
  257.                        * after setting current_finished */
  258. static colnr_t    current_col = 0;    /* column of current state */
  259. static int    current_finished = 0;    /* current line has been finished */
  260. static struct growarray current_state    /* current stack of state_items */
  261.         = {0, 0, 0, 0, NULL};
  262. static short    *current_next_list = NULL; /* when non-zero, nextgroup list */
  263. static int    current_next_flags = 0; /* flags for current_next_list */
  264. static int    current_line_id = 0;    /* unique number for current line */
  265.  
  266. #define CUR_STATE(idx)    ((struct state_item *)(current_state.ga_data))[idx]
  267.  
  268. static void syn_sync __ARGS((WIN *wp, linenr_t lnum));
  269. static int syn_match_linecont __ARGS((linenr_t lnum));
  270. static void syn_start_line __ARGS((void));
  271. static void syn_free_all_states __ARGS((BUF *buf));
  272. static void syn_clear_states __ARGS((int start, int end));
  273. static void store_current_state __ARGS((void));
  274. static void invalidate_current_state __ARGS((void));
  275. static void validate_current_state __ARGS((void));
  276. static void copy_state_to_current __ARGS((struct syn_state *from));
  277. static void move_state __ARGS((int from, int to));
  278. static int syn_finish_line __ARGS((int syncing));
  279. static int syn_current_attr __ARGS((int syncing, char_u *line));
  280. static int did_match_already __ARGS((int idx));
  281. static struct state_item *push_next_match __ARGS((struct state_item *cur_si, char_u *line));
  282. static void check_state_ends __ARGS((char_u *line));
  283. static void update_si_attr __ARGS((int idx));
  284. static void check_keepend __ARGS((void));
  285. static void update_si_end __ARGS((struct state_item *sip, char_u *line, int startcol));
  286. static short *copy_id_list __ARGS((short *list));
  287. static int in_id_list __ARGS((short *cont_list, int id, int contained));
  288. static int push_current __ARGS((int idx));
  289. static void pop_current __ARGS((void));
  290. static char_u *find_endp __ARGS((int idx, char_u *sstart, int at_bol, char_u **hl_endp, int *flagsp, char_u **end_endp, int *end_idx));
  291. static char_u *syn_add_end_off __ARGS((struct syn_pattern *spp, int idx, int extra));
  292. static char_u *syn_add_start_off __ARGS((struct syn_pattern *spp, int idx, int extra));
  293. static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, int *flags, short **next_list, struct state_item *cur_si));
  294. static void syn_cmd_case __ARGS((EXARG *eap, int syncing));
  295. static void syntax_sync_clear __ARGS((void));
  296. static void syn_remove_pattern __ARGS((BUF *buf, int idx));
  297. static void syn_clear_pattern __ARGS((BUF *buf, int i));
  298. static void syn_cmd_clear __ARGS((EXARG *eap, int syncing));
  299. static void syn_clear_one __ARGS((int id, int syncing));
  300. static void syn_cmd_on __ARGS((EXARG *eap, int syncing));
  301. static void syn_cmd_off __ARGS((EXARG *eap, int syncing));
  302. static void syn_cmd_list __ARGS((EXARG *eap, int syncing));
  303. static void syn_lines_msg __ARGS((void));
  304. static void syn_list_one __ARGS((int id, int syncing, int link_only));
  305. static void put_id_list __ARGS((char_u *name, short *list, int attr));
  306. static void put_pattern __ARGS((char *s, int c, struct syn_pattern *spp, int attr));
  307. static int syn_list_keywords __ARGS((int id, struct keyentry **ktabp, int did_header, int attr));
  308. static void syn_clear_keyword __ARGS((int id, struct keyentry **ktabp));
  309. static void free_keywtab __ARGS((struct keyentry **ktabp));
  310. static void add_keyword __ARGS((char_u *name, int id, int flags, short *next_list));
  311. static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
  312. static char_u *get_syn_options __ARGS((char_u *arg, int *flagsp, int *sync_idx,
  313.                     short **cont_list, short **next_list));
  314. static void syn_cmd_keyword __ARGS((EXARG *eap, int syncing));
  315. static void syn_cmd_match __ARGS((EXARG *eap, int syncing));
  316. static void syn_cmd_region __ARGS((EXARG *eap, int syncing));
  317. static void init_syn_patterns __ARGS((void));
  318. static char_u *get_syn_pattern __ARGS((char_u *arg, struct syn_pattern    *ci));
  319. static void syn_cmd_sync __ARGS((EXARG *eap, int syncing));
  320. static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
  321.  
  322. /*
  323.  * Start the syntax recognition for a line.  This function is normally called
  324.  * from the screen updating, once for each consecutive line.
  325.  * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
  326.  * it.    Careful: curbuf and curwin are likely to point to another buffer and
  327.  * window.
  328.  */
  329.     void
  330. syntax_start(wp, lnum)
  331.     WIN        *wp;
  332.     linenr_t    lnum;
  333. {
  334.     long        to, from;
  335.     long        diff;
  336.     int            idx;
  337.  
  338.     reg_syn = TRUE;    /* let vim_regexec() know we're using syntax */
  339.  
  340.     /*
  341.      * After switching buffers, invalidate current_state.
  342.      */
  343.     if (syn_buf != wp->w_buffer)
  344.     {
  345.     invalidate_current_state();
  346.     syn_buf = wp->w_buffer;
  347.     }
  348.  
  349.     /*
  350.      * If the screen height has changed, re-allocate b_syn_states[].
  351.      * Use the screen height plus one, so the line above and below the window
  352.      * can always be stored too.
  353.      */
  354.     if (syn_buf->b_syn_states_len != Rows + 1)
  355.     {
  356.     syn_free_all_states(syn_buf);
  357.     syn_buf->b_syn_states = (struct syn_state *)alloc_clear(
  358.                 (int)((Rows + 1) * sizeof(struct syn_state)));
  359.     if (syn_buf->b_syn_states == NULL)        /* out of memory */
  360.     {
  361.         syn_buf->b_syn_states_len = 0;
  362.         goto theend;
  363.     }
  364.     syn_buf->b_syn_states_len = Rows + 1;
  365.     syn_buf->b_syn_states_lnum = 0;
  366.     syn_buf->b_syn_change_lnum = MAXLNUM;
  367.     }
  368.  
  369.     /*
  370.      * Remove items from b_syn_states[] that have changes in or before them.
  371.      */
  372.     if (syn_buf->b_syn_change_lnum != MAXLNUM)
  373.     {
  374.     /* if change is before the end of the array, something to clear */
  375.     if (syn_buf->b_syn_change_lnum <
  376.            syn_buf->b_syn_states_lnum + syn_buf->b_syn_states_len - 1)
  377.     {
  378.         /* line before the start changed: clear all entries */
  379.         if (syn_buf->b_syn_change_lnum < syn_buf->b_syn_states_lnum)
  380.         idx = 0;
  381.         else
  382.         idx = syn_buf->b_syn_change_lnum -
  383.                            syn_buf->b_syn_states_lnum + 1;
  384.         syn_clear_states(idx, syn_buf->b_syn_states_len);
  385.     }
  386.     if (syn_buf->b_syn_change_lnum < current_lnum)
  387.         invalidate_current_state();
  388.     syn_buf->b_syn_change_lnum = MAXLNUM;
  389.     }
  390.  
  391.     /*
  392.      * If the topline has changed out of range of b_syn_states[], move the
  393.      * items in the array.
  394.      */
  395.     if (wp->w_topline < syn_buf->b_syn_states_lnum)
  396.     {
  397.     /*
  398.      * Topline is above the array: Move entries down.
  399.      * (w_topline - 1) is the new first line in * b_syn_states[].
  400.      */
  401.     to = syn_buf->b_syn_states_len - 1;
  402.     from = to - (syn_buf->b_syn_states_lnum - (wp->w_topline - 1));
  403.     while (from >= 0)
  404.     {
  405.         move_state((int)from, (int)to);
  406.         --from;
  407.         --to;
  408.     }
  409.     syn_clear_states(0, (int)(to + 1));
  410.     syn_buf->b_syn_states_lnum = wp->w_topline - 1;
  411.     }
  412.     else if ((diff = (wp->w_topline + wp->w_height) -
  413.          (syn_buf->b_syn_states_lnum + syn_buf->b_syn_states_len)) > 0)
  414.     {
  415.     /*
  416.      * The last line in the window is below the array: Move entries up
  417.      * "diff" positions.
  418.      */
  419.     to = 0;
  420.     from = to + diff;
  421.     while (from < syn_buf->b_syn_states_len)
  422.     {
  423.         move_state((int)from, (int)to);
  424.         ++from;
  425.         ++to;
  426.     }
  427.     syn_clear_states((int)to, syn_buf->b_syn_states_len);
  428.     syn_buf->b_syn_states_lnum += diff;
  429.     }
  430.  
  431.     /*
  432.      * If the state of the end of the previous line is useful, store it.
  433.      */
  434.     if (VALID_STATE(¤t_state) && current_lnum < lnum &&
  435.         current_lnum >= syn_buf->b_syn_states_lnum &&
  436.         current_lnum <
  437.              syn_buf->b_syn_states_lnum + syn_buf->b_syn_states_len &&
  438.         current_lnum < syn_buf->b_ml.ml_line_count)
  439.     {
  440.     (void)syn_finish_line(FALSE);
  441.     if (!current_state_stored)
  442.     {
  443.         ++current_lnum;
  444.         store_current_state();
  445.     }
  446.  
  447.     /*
  448.      * If the current_lnum is now the same as "lnum", keep the current
  449.      * state (this happens very often!).  Otherwise invalidate
  450.      * current_state and figure it out below.
  451.      */
  452.     if (current_lnum != lnum)
  453.         invalidate_current_state();
  454.     }
  455.     else
  456.     invalidate_current_state();
  457.  
  458.  
  459.     /*
  460.      * Try to synchronize from a saved state in b_syn_states[].
  461.      * Only do this if lnum is not before and not to far beyond a saved state.
  462.      */
  463.     if (INVALID_STATE(¤t_state))
  464.     {
  465.     diff = syn_buf->b_syn_sync_minlines;
  466.     if (diff < Rows * 2)
  467.         diff = Rows * 2;        /* parse less then two screenfulls extra */
  468.     if (lnum >= syn_buf->b_syn_states_lnum &&
  469.         lnum <= syn_buf->b_syn_states_lnum +
  470.                          syn_buf->b_syn_states_len + diff)
  471.     {
  472.         idx = lnum - syn_buf->b_syn_states_lnum;
  473.         if (idx >= syn_buf->b_syn_states_len)
  474.         idx = syn_buf->b_syn_states_len - 1;
  475.         for ( ; idx >= 0; --idx)
  476.         {
  477.         if (VALID_STATE(&syn_buf->b_syn_states[idx].sst_ga))
  478.         {
  479.             current_lnum = syn_buf->b_syn_states_lnum + idx;
  480.             copy_state_to_current(&(syn_buf->b_syn_states[idx]));
  481.             break;
  482.         }
  483.         }
  484.     }
  485.     }
  486.  
  487.     /*
  488.      * If "lnum" is before or far beyond a line with a saved state, need to
  489.      * re-synchronize.
  490.      */
  491.     if (INVALID_STATE(¤t_state))
  492.     syn_sync(wp, lnum);
  493.  
  494.     /*
  495.      * Advance from the sync point or saved state until the current line.
  496.      */
  497.     while (current_lnum < lnum)
  498.     {
  499.     syn_start_line();
  500.     (void)syn_finish_line(FALSE);
  501.     ++current_lnum;
  502.     store_current_state();
  503.     }
  504.  
  505.     syn_start_line();
  506.  
  507. theend:
  508.     reg_syn = FALSE;
  509. }
  510.  
  511. /*
  512.  * Try to find a synchronisation point for line "lnum".
  513.  *
  514.  * This sets current_lnum and the current state.  One of three methods is
  515.  * used:
  516.  * 1. Search backwards for the end of a C-comment.
  517.  * 2. Search backwards for given sync patterns.
  518.  * 3. Simply start on a given number of lines above "lnum".
  519.  */
  520.     static void
  521. syn_sync(wp, start_lnum)
  522.     WIN        *wp;
  523.     linenr_t    start_lnum;
  524. {
  525.     BUF            *curbuf_save;
  526.     WIN            *curwin_save;
  527.     FPOS        cursor_save;
  528.     int            idx;
  529.     linenr_t        lnum;
  530.     linenr_t        end_lnum;
  531.     linenr_t        break_lnum;
  532.     int            had_sync_point = FALSE;
  533.     struct state_item    *cur_si;
  534.     struct syn_pattern    *spp;
  535.     char_u        *line;
  536.     int            found_flags = 0;
  537.     int            found_match_idx = 0;
  538.     linenr_t        found_current_lnum = 0;
  539.     int            found_current_col= 0;
  540.     colnr_t        found_m_endcol = 0;
  541.  
  542.     /*
  543.      * Clear any current state that might be hanging around.
  544.      */
  545.     invalidate_current_state();
  546.  
  547.     /*
  548.      * Start at least "minlines" back.
  549.      * Default starting point for parsing is there.
  550.      */
  551.     start_lnum -= syn_buf->b_syn_sync_minlines;
  552.     if (start_lnum < 1)
  553.     start_lnum = 1;
  554.     current_lnum = start_lnum;
  555.  
  556.     /*
  557.      * 1. Search backwards for the end of a C-style comment.
  558.      */
  559.     if (syn_buf->b_syn_sync_flags & SF_CCOMMENT)
  560.     {
  561.     /* need to make syn_buf the current buffer for a moment */
  562.     curwin_save = curwin;
  563.     curwin = wp;
  564.     curbuf_save = curbuf;
  565.     curbuf = syn_buf;
  566.  
  567.     /*
  568.      * Skip lines that end in a backslash.
  569.      */
  570.     for ( ; start_lnum > 1; --start_lnum)
  571.     {
  572.         line = ml_get(start_lnum - 1);
  573.         if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
  574.         break;
  575.     }
  576.     current_lnum = start_lnum;
  577.  
  578.     /* set cursor to start of search */
  579.     cursor_save = wp->w_cursor;
  580.     wp->w_cursor.lnum = start_lnum;
  581.     wp->w_cursor.col = 0;
  582.  
  583.     /*
  584.      * If the line is inside a comment, need to find the syntax item that
  585.      * defines the comment.
  586.      * Restrict the search for the end of a comment to b_syn_sync_maxlines.
  587.      */
  588.     if (find_start_comment((int)syn_buf->b_syn_sync_maxlines) != NULL)
  589.     {
  590.         for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
  591.         if (SYN_ITEMS(syn_buf)[idx].sp_syn_id ==
  592.                              syn_buf->b_syn_sync_id &&
  593.                   SYN_ITEMS(syn_buf)[idx].sp_type == SPTYPE_START)
  594.         {
  595.             validate_current_state();
  596.             if (push_current(idx) == OK)
  597.             update_si_attr(current_state.ga_len - 1);
  598.             break;
  599.         }
  600.     }
  601.  
  602.     /* restore cursor and buffer */
  603.     wp->w_cursor = cursor_save;
  604.     curwin = curwin_save;
  605.     curbuf = curbuf_save;
  606.     }
  607.  
  608.     /*
  609.      * 2. Search backwards for given sync patterns.
  610.      */
  611.     else if (syn_buf->b_syn_sync_flags & SF_MATCH)
  612.     {
  613.     if (syn_buf->b_syn_sync_maxlines
  614.                  && start_lnum > syn_buf->b_syn_sync_maxlines)
  615.         break_lnum = start_lnum - syn_buf->b_syn_sync_maxlines;
  616.     else
  617.         break_lnum = 0;
  618.  
  619.     end_lnum = start_lnum;
  620.     lnum = start_lnum;
  621.     while (--lnum > break_lnum)
  622.     {
  623.         /*
  624.          * Check if the previous line has the line-continuation pattern.
  625.          */
  626.         if (lnum > 1 && syn_match_linecont(lnum - 1))
  627.         continue;
  628.  
  629.         /*
  630.          * Start with nothing on the state stack
  631.          */
  632.         validate_current_state();
  633.  
  634.         for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
  635.         {
  636.         syn_start_line();
  637.         for (;;)
  638.         {
  639.             had_sync_point = syn_finish_line(TRUE);
  640.             /*
  641.              * When a sync point has been found, remember where, and
  642.              * continue to look for another one, further on in the line.
  643.              */
  644.             if (had_sync_point && current_state.ga_len)
  645.             {
  646.             cur_si = &CUR_STATE(current_state.ga_len - 1);
  647.             spp = &(SYN_ITEMS(syn_buf)[cur_si->si_idx]);
  648.             found_flags = spp->sp_flags;
  649.             found_match_idx = spp->sp_sync_idx;
  650.             found_current_lnum = current_lnum;
  651.             found_current_col = current_col;
  652.             found_m_endcol = cur_si->si_m_endcol;
  653.             /*
  654.              * Continue after the match (be aware of a zero-length
  655.              * match).
  656.              */
  657.             if (found_m_endcol > current_col)
  658.                 current_col = found_m_endcol;
  659.             else
  660.                 ++current_col;
  661.  
  662.             /* syn_current_attr() will have skipped the check for
  663.              * an item that ends here, need to do that now. */
  664.             ++current_col;
  665.             check_state_ends(
  666.                     ml_get_buf(syn_buf, current_lnum, FALSE));
  667.             --current_col;
  668.             }
  669.             else
  670.             break;
  671.         }
  672.         }
  673.  
  674.         /*
  675.          * If a sync point was encountered, break here.
  676.          */
  677.         if (found_flags)
  678.         {
  679.         /*
  680.          * Put the item that was specified by the sync point on the
  681.          * state stack.  If there was no item specified, make the
  682.          * state stack empty.
  683.          */
  684.         ga_clear(¤t_state);
  685.         if (found_match_idx >= 0 &&
  686.             push_current(found_match_idx) == OK)
  687.             update_si_attr(current_state.ga_len - 1);
  688.  
  689.         /*
  690.          * When using "grouphere", continue from the sync point
  691.          * match, until the end of the line.  Parsing starts at
  692.          * the next line.
  693.          * For "groupthere" the parsing starts at start_lnum.
  694.          */
  695.         if (found_flags & HL_SYNC_HERE)
  696.         {
  697.             if (current_state.ga_len)
  698.             {
  699.             cur_si = &CUR_STATE(current_state.ga_len - 1);
  700.             cur_si->si_h_startcol = found_current_col;
  701.             line = ml_get_buf(syn_buf, current_lnum, FALSE);
  702.             update_si_end(cur_si, line, current_col);
  703.             }
  704.             current_col = found_m_endcol;
  705.             current_lnum = found_current_lnum;
  706.             (void)syn_finish_line(FALSE);
  707.             ++current_lnum;
  708.         }
  709.         else
  710.             current_lnum = start_lnum;
  711.  
  712.         break;
  713.         }
  714.  
  715.         end_lnum = lnum;
  716.         invalidate_current_state();
  717.     }
  718.  
  719.     /* Ran into start of the file or exceeded maximum number of lines */
  720.     if (lnum <= break_lnum)
  721.     {
  722.         invalidate_current_state();
  723.         current_lnum = break_lnum + 1;
  724.     }
  725.     }
  726.  
  727.     validate_current_state();
  728. }
  729.  
  730. /*
  731.  * Return TRUE if the line-continuation pattern matches in line "lnum".
  732.  */
  733.     static int
  734. syn_match_linecont(lnum)
  735.     linenr_t        lnum;
  736. {
  737.     if (syn_buf->b_syn_linecont_prog != NULL)
  738.     {
  739.     reg_ic = syn_buf->b_syn_linecont_ic;
  740.     return vim_regexec(syn_buf->b_syn_linecont_prog,
  741.                       ml_get_buf(syn_buf, lnum, FALSE), TRUE);
  742.     }
  743.     return FALSE;
  744. }
  745.  
  746. /*
  747.  * Set the state for the start of a line.
  748.  */
  749.     static void
  750. syn_start_line()
  751. {
  752.     char_u        *line;
  753.     struct state_item    *cur_si;
  754.     int            i;
  755.  
  756.     current_finished = FALSE;
  757.     current_col = 0;
  758.  
  759.     /*
  760.      * Need to update the end of a start/skip/end that continues from the
  761.      * previous line.  And regions that have "keepend", because they may
  762.      * influence contained items.
  763.      * Then check for items ending in column 0.
  764.      */
  765.     if (current_state.ga_len)
  766.     {
  767.     line = ml_get_buf(syn_buf, current_lnum, FALSE);
  768.     for (i = 0; i < current_state.ga_len; ++i)
  769.     {
  770.         cur_si = &CUR_STATE(i);
  771.         if ((cur_si->si_flags & HL_KEEPEND)
  772.                          || i == current_state.ga_len - 1)
  773.         {
  774.         cur_si->si_h_startcol = 0;    /* start highl. in col 0 */
  775.         update_si_end(cur_si, line, 0);
  776.         }
  777.     }
  778.     check_state_ends(line);
  779.     }
  780.     next_match_idx = -1;
  781.     ++current_line_id;
  782. }
  783.  
  784. /*
  785.  * Free b_syn_states[] for buffer "buf".
  786.  */
  787.     static void
  788. syn_free_all_states(buf)
  789.     BUF        *buf;
  790. {
  791.     int        idx;
  792.  
  793.     if (buf->b_syn_states != NULL)
  794.     {
  795.     for (idx = 0; idx < buf->b_syn_states_len; ++idx)
  796.         ga_clear(&(buf->b_syn_states[idx].sst_ga));
  797.     vim_free(buf->b_syn_states);
  798.     buf->b_syn_states = NULL;
  799.     buf->b_syn_states_len = 0;
  800.     }
  801. }
  802.  
  803. /*
  804.  * clear the entries in b_syn_states[] from "start" to (not including) "end"
  805.  */
  806.     static void
  807. syn_clear_states(start, end)
  808.     int        start, end;
  809. {
  810.     int        idx;
  811.     struct    growarray *sp;
  812.  
  813.     for (idx = start; idx < end; ++idx)
  814.     {
  815.     sp = &(syn_buf->b_syn_states[idx].sst_ga);
  816.     ga_clear(sp);
  817.     sp->ga_itemsize = 0;
  818.     }
  819. }
  820.  
  821. /*
  822.  * Try saving the current state in b_syn_states[].
  823.  * The current state must be at the start of the current_lnum line!
  824.  */
  825.     static void
  826. store_current_state()
  827. {
  828.     long        idx;
  829.     int            i;
  830.     struct growarray    *to;
  831.  
  832.     idx = current_lnum - syn_buf->b_syn_states_lnum;
  833.     if (idx >= 0 && idx < syn_buf->b_syn_states_len)
  834.     {
  835.     to = &(syn_buf->b_syn_states[idx].sst_ga);
  836.     if (to->ga_data != NULL)
  837.         ga_clear(to);
  838.     else if (to->ga_itemsize == 0)
  839.     {
  840.         to->ga_itemsize = sizeof(struct buf_state);
  841.         to->ga_growsize = 3;
  842.     }
  843.     if (current_state.ga_len && ga_grow(to, current_state.ga_len) != FAIL)
  844.     {
  845.         for (i = 0; i < current_state.ga_len; ++i)
  846.         {
  847.         SYN_STATE_P(to)[i].bs_idx = CUR_STATE(i).si_idx;
  848.         SYN_STATE_P(to)[i].bs_flags = CUR_STATE(i).si_flags;
  849.         }
  850.         to->ga_len = current_state.ga_len;
  851.         to->ga_room -= to->ga_len;
  852.     }
  853.     syn_buf->b_syn_states[idx].sst_next_list = current_next_list;
  854.     syn_buf->b_syn_states[idx].sst_next_flags = current_next_flags;
  855.     }
  856.     current_state_stored = TRUE;
  857. }
  858.  
  859. /*
  860.  * Copy a state stack from "from" in b_syn_states[] to current_state;
  861.  */
  862.     static void
  863. copy_state_to_current(from)
  864.     struct syn_state *from;
  865. {
  866.     int        i;
  867.     struct growarray    *ga = &(from->sst_ga);
  868.  
  869.     ga_clear(¤t_state);
  870.     validate_current_state();
  871.     if (ga->ga_len && ga_grow(¤t_state, ga->ga_len) != FAIL)
  872.     {
  873.     for (i = 0; i < ga->ga_len; ++i)
  874.     {
  875.         CUR_STATE(i).si_idx = SYN_STATE_P(ga)[i].bs_idx;
  876.         CUR_STATE(i).si_flags = SYN_STATE_P(ga)[i].bs_flags;
  877.         CUR_STATE(i).si_m_endcol = 0;
  878.         CUR_STATE(i).si_m_startcol = 0;
  879.         CUR_STATE(i).si_m_lnum = 0;
  880.         update_si_attr(i);
  881.     }
  882.     current_state.ga_len = ga->ga_len;
  883.     current_state.ga_room -= current_state.ga_len;
  884.     }
  885.     current_next_list = from->sst_next_list;
  886.     current_next_flags = from->sst_next_flags;
  887. }
  888.  
  889.     static void
  890. invalidate_current_state()
  891. {
  892.     ga_clear(¤t_state);
  893.     current_state.ga_itemsize = 0;
  894.     current_next_list = NULL;
  895. }
  896.  
  897.     static void
  898. validate_current_state()
  899. {
  900.     current_state.ga_itemsize = sizeof(struct state_item);
  901.     current_state.ga_growsize = 3;
  902. }
  903.  
  904. /*
  905.  * Move a state stack from b_syn_states[from] to b_syn_states[to].
  906.  */
  907.     static void
  908. move_state(from, to)
  909.     int        from, to;
  910. {
  911.     ga_clear(&(syn_buf->b_syn_states[to].sst_ga));
  912.     syn_buf->b_syn_states[to] = syn_buf->b_syn_states[from];
  913.     ga_init(&(syn_buf->b_syn_states[from].sst_ga));
  914.     syn_buf->b_syn_states[from].sst_ga.ga_itemsize = 0;    /* invalid entry */
  915. }
  916.  
  917. /*
  918.  * Return TRUE if the syntax at start of lnum changed since last time.
  919.  * This will only be called just after get_syntax_attr for the previous line,
  920.  * to check if the next line needs to be redrawn too.
  921.  */
  922.     int
  923. syntax_check_changed(lnum)
  924.     linenr_t    lnum;
  925. {
  926.     struct growarray    *ssp;
  927.     int            i;
  928.     int            retval = TRUE;
  929.     long        idx;
  930.  
  931.     reg_syn = TRUE;    /* let vim_regexec() know we're using syntax */
  932.  
  933.     /*
  934.      * Check the state stack when:
  935.      * - lnum is just below the previously syntaxed line.
  936.      * - lnum is not before the lines with saved states.
  937.      * - lnum is not past the lines with saved states.
  938.      * - lnum is at or before the last changed line.
  939.      */
  940.     idx = lnum - syn_buf->b_syn_states_lnum;
  941.     if (VALID_STATE(¤t_state) && lnum == current_lnum + 1 &&
  942.         idx >= 0 && idx < syn_buf->b_syn_states_len &&
  943.         lnum < syn_buf->b_syn_change_lnum)
  944.     {
  945.     /*
  946.      * finish the previous line (needed when not all of the line was drawn)
  947.      */
  948.     (void)syn_finish_line(FALSE);
  949.  
  950.     ssp = &(syn_buf->b_syn_states[idx].sst_ga);
  951.     if (VALID_STATE(ssp))    /* entry is valid */
  952.     {
  953.         /*
  954.          * Compare the current state with the previously saved state of
  955.          * the line.
  956.          */
  957.         if (ssp->ga_len == current_state.ga_len
  958.             && syn_buf->b_syn_states[idx].sst_next_list
  959.                              == current_next_list)
  960.         {
  961.         for (i = current_state.ga_len; --i >= 0; )
  962.             if (SYN_STATE_P(ssp)[i].bs_idx != CUR_STATE(i).si_idx)
  963.             break;
  964.         /*
  965.          * If still the same state, return FALSE, syntax didn't change.
  966.          */
  967.         if (i < 0)
  968.             retval = FALSE;
  969.         }
  970.     }
  971.  
  972.     /*
  973.      * Store the current state in b_syn_states[] for later use.
  974.      */
  975.     ++current_lnum;
  976.     store_current_state();
  977.     }
  978.  
  979.     reg_syn = FALSE;
  980.     return retval;
  981. }
  982.  
  983. /*
  984.  * Finish the current line.
  985.  * This doesn't return any attributes, it only gets the state at the end of
  986.  * the line.  It can start anywhere in the line, as long as the current state
  987.  * is valid.
  988.  */
  989.     static int
  990. syn_finish_line(syncing)
  991.     int        syncing;        /* called for syncing */
  992. {
  993.     char_u        *line;
  994.     struct state_item    *cur_si;
  995.  
  996.     if (!current_finished)
  997.     {
  998.     line = ml_get_buf(syn_buf, current_lnum, FALSE);
  999.     while (!current_finished)
  1000.     {
  1001.         (void)syn_current_attr(syncing, line);
  1002.         /*
  1003.          * When syncing, and found some item, need to check the item.
  1004.          */
  1005.         if (syncing && current_state.ga_len)
  1006.         {
  1007.         /*
  1008.          * Check for match with sync item.
  1009.          */
  1010.         cur_si = &CUR_STATE(current_state.ga_len - 1);
  1011.         if (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
  1012.                            & (HL_SYNC_HERE|HL_SYNC_THERE))
  1013.             return TRUE;
  1014.  
  1015.         /* syn_current_attr() will have skipped the check for an item
  1016.          * that ends here, need to do that now. */
  1017.         ++current_col;
  1018.         check_state_ends(line);
  1019.         --current_col;
  1020.         }
  1021.         ++current_col;
  1022.     }
  1023.     }
  1024.     return FALSE;
  1025. }
  1026.  
  1027. /*
  1028.  * Return highlight attributes for next character.
  1029.  * This function is alwyas called from the screen updating, for each
  1030.  * consecutive character.  And only after syntax_start() has been called for
  1031.  * the current line.
  1032.  * Note that "col" doesn't start at 0, when win->w_leftcol is non-zero, and
  1033.  * doesn't continue until the last col when 'nowrap' is set.
  1034.  */
  1035.     int
  1036. get_syntax_attr(col, line)
  1037.     colnr_t    col;
  1038.     char_u    *line;
  1039. {
  1040.     int        attr = 0;
  1041.  
  1042.     reg_syn = TRUE;    /* let vim_regexec() know we're using syntax */
  1043.  
  1044.     /* check for out of memory situation */
  1045.     if (syn_buf->b_syn_states_len == 0)
  1046.     return 0;
  1047.  
  1048.     /* Make sure current_state is valid */
  1049.     if (INVALID_STATE(¤t_state))
  1050.     validate_current_state();
  1051.  
  1052.     /*
  1053.      * Skip from the current column to "col", get the attributes for "col".
  1054.      */
  1055.     while (current_col <= col)
  1056.     {
  1057.     attr = syn_current_attr(FALSE, line);
  1058.     ++current_col;
  1059.     }
  1060.  
  1061.     reg_syn = FALSE;
  1062.     return attr;
  1063. }
  1064.  
  1065. /*
  1066.  * Get syntax attributes for current_lnum, current_col.
  1067.  */
  1068.     static int
  1069. syn_current_attr(syncing, line)
  1070.     int        syncing;        /* When 1: called for syncing */
  1071.     char_u    *line;
  1072. {
  1073.     int            syn_id;
  1074.     char_u        *endp;
  1075.     char_u        *hl_endp = NULL;
  1076.     char_u        *eoep;        /* end-of-end pattern */
  1077.     int            end_idx;    /* group ID for end pattern */
  1078.     int            idx;
  1079.     struct syn_pattern    *spp;
  1080.     struct state_item    *cur_si, *sip;
  1081.     int            startcol;
  1082.     int            hl_startcol;
  1083.     int            eos_col;    /* end-of-start column */
  1084.     int            endcol;
  1085.     int            flags;
  1086.     short        *next_list;
  1087.     int            found_match;            /* found usable match */
  1088.     static int        try_next_column = FALSE;    /* must try in next col */
  1089.  
  1090.     /*
  1091.      * No character, no attributes!  Past end of line?
  1092.      * Do try matching with an empty line (could be the start of a region).
  1093.      */
  1094.     if (*(line + current_col) == NUL && current_col != 0)
  1095.     {
  1096.     /*
  1097.      * If we found a match after the last column, use it.
  1098.      */
  1099.     if (next_match_idx >= 0 && next_match_col >= (int)current_col
  1100.                           && next_match_col != MAXCOL)
  1101.         (void)push_next_match(NULL, line);
  1102.  
  1103.     current_finished = TRUE;
  1104.     current_state_stored = FALSE;
  1105.     return 0;
  1106.     }
  1107.  
  1108.     /* if the next character is NUL, we will finish the line now */
  1109.     if (*(line + current_col) == NUL || *(line + current_col + 1) == NUL)
  1110.     {
  1111.     current_finished = TRUE;
  1112.     current_state_stored = FALSE;
  1113.     }
  1114.  
  1115.     /*
  1116.      * When in the previous column there was a match but it could not be used
  1117.      * (empty match or already matched in this column) need to try again in
  1118.      * the next column.
  1119.      */
  1120.     if (try_next_column)
  1121.     {
  1122.     next_match_idx = -1;
  1123.     try_next_column = FALSE;
  1124.     }
  1125.  
  1126.     /*
  1127.      * Repeat matching keywords and patterns, to find contained items at the
  1128.      * same column.  This stops when there are no extra matches at the current
  1129.      * column.
  1130.      */
  1131.     do
  1132.     {
  1133.     found_match = FALSE;
  1134.     syn_id = 0;
  1135.  
  1136.     /*
  1137.      * 1. Check for a current state.
  1138.      *    Only when there is no current state, or if the current state may
  1139.      *    contain other things, we need to check for keywords and patterns.
  1140.      */
  1141.     if (current_state.ga_len)
  1142.         cur_si = &CUR_STATE(current_state.ga_len - 1);
  1143.     else
  1144.         cur_si = NULL;
  1145.  
  1146.     if (cur_si == NULL || cur_si->si_cont_list != NULL)
  1147.     {
  1148.         /*
  1149.          * 2. Check for keywords, if on a keyword char after a non-keyword
  1150.          *      char.  Don't do this when syncing.
  1151.          */
  1152.         if (       !syncing
  1153.             && (syn_buf->b_keywtab != NULL
  1154.             || syn_buf->b_keywtab_ic != NULL)
  1155.             && vim_iswordc_buf(line[current_col], syn_buf)
  1156.             && (current_col == 0
  1157.             || !vim_iswordc_buf(line[current_col - 1], syn_buf)))
  1158.         {
  1159.         syn_id = check_keyword_id(line, (int)current_col,
  1160.                      &endcol, &flags, &next_list, cur_si);
  1161.         if (syn_id)
  1162.         {
  1163.             if (push_current(KEYWORD_IDX) == OK)
  1164.             {
  1165.             cur_si = &CUR_STATE(current_state.ga_len - 1);
  1166.             cur_si->si_m_startcol = current_col;
  1167.             cur_si->si_h_startcol = 0;    /* starts right away */
  1168.             cur_si->si_m_endcol = endcol;
  1169.             cur_si->si_h_endcol = endcol;
  1170.             cur_si->si_ends = TRUE;
  1171.             cur_si->si_end_idx = 0;
  1172.             cur_si->si_flags = flags;
  1173.             cur_si->si_id = syn_id;
  1174.             cur_si->si_trans_id = syn_id;
  1175.             if (flags & HL_TRANSP)
  1176.             {
  1177.                 if (current_state.ga_len < 2)
  1178.                 {
  1179.                 cur_si->si_attr = 0;
  1180.                 cur_si->si_trans_id = 0;
  1181.                 }
  1182.                 else
  1183.                 {
  1184.                 cur_si->si_attr = CUR_STATE(
  1185.                     current_state.ga_len - 2).si_attr;
  1186.                 cur_si->si_trans_id = CUR_STATE(
  1187.                     current_state.ga_len - 2).si_trans_id;
  1188.                 }
  1189.             }
  1190.             else
  1191.                 cur_si->si_attr = syn_id2attr(syn_id);
  1192.             cur_si->si_cont_list = NULL;
  1193.             cur_si->si_next_list = next_list;
  1194.             check_keepend();
  1195.             }
  1196.             else
  1197.             vim_free(next_list);
  1198.         }
  1199.         }
  1200.  
  1201.         /*
  1202.          * 3. Check for patterns (only if not found a keyword).
  1203.          */
  1204.         if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
  1205.         {
  1206.         /*
  1207.          * If we didn't check for a match yet, or we are past it, check
  1208.          * for any match with a pattern.
  1209.          */
  1210.         if (next_match_idx < 0 || next_match_col < (int)current_col)
  1211.         {
  1212.             /*
  1213.              * Check all relevant patterns for a match at this
  1214.              * position.
  1215.              */
  1216.             next_match_idx = 0;        /* no match in this line yet */
  1217.             next_match_col = MAXCOL;    /* no match in this line yet */
  1218.             for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
  1219.             {
  1220.             spp = &(SYN_ITEMS(syn_buf)[idx]);
  1221.             if (       spp->sp_syncing == syncing
  1222.                 && (spp->sp_type == SPTYPE_MATCH
  1223.                     || spp->sp_type == SPTYPE_START)
  1224.                 && ((current_next_list != 0
  1225.                     && in_id_list(current_next_list,
  1226.                         spp->sp_syn_id, 0))
  1227.                     || (current_next_list == 0
  1228.                     && ((cur_si == NULL
  1229.                         && !(spp->sp_flags & HL_CONTAINED))
  1230.                         || (cur_si != NULL
  1231.                      && in_id_list(cur_si->si_cont_list,
  1232.                         spp->sp_syn_id,
  1233.                          spp->sp_flags & HL_CONTAINED))))))
  1234.             {
  1235.                 int lc_col;
  1236.  
  1237.                 /* If we already tried matching in this line, and
  1238.                  * there isn't a match before next_match_col, skip
  1239.                  * this item. */
  1240.                 if (spp->sp_line_id == current_line_id
  1241.                     && spp->sp_startcol >= next_match_col)
  1242.                 continue;
  1243.                 spp->sp_line_id = current_line_id;
  1244.  
  1245.                 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
  1246.                 if (lc_col < 0)
  1247.                 lc_col = 0;
  1248.  
  1249.                 reg_ic = spp->sp_ic;
  1250.                 if (!vim_regexec(spp->sp_prog, line + lc_col,
  1251.                          lc_col == 0))
  1252.                 {
  1253.                 spp->sp_startcol = MAXCOL;
  1254.                 continue;
  1255.                 }
  1256.  
  1257.                 /*
  1258.                  * Compute the first column of the match.
  1259.                  */
  1260.                 startcol = syn_add_start_off(spp,
  1261.                                SPO_MS_OFF, -1) - line;
  1262.                 if (startcol < 0)
  1263.                 startcol = 0;
  1264.                 spp->sp_startcol = startcol;
  1265.  
  1266.                 /*
  1267.                  * If an existing match is better, skip this one.
  1268.                  */
  1269.                 if (startcol >= next_match_col)
  1270.                 continue;
  1271.  
  1272.                 /*
  1273.                  * If we matched this pattern at this position
  1274.                  * before, skip it.  Must retry in the next
  1275.                  * column, because it may match from there..
  1276.                  */
  1277.                 if (did_match_already(idx))
  1278.                 {
  1279.                 try_next_column = TRUE;
  1280.                 continue;
  1281.                 }
  1282.  
  1283.                 endp = spp->sp_prog->endp[0];
  1284.  
  1285.                 /* Compute the highlight start. */
  1286.                 hl_startcol = syn_add_start_off(spp,
  1287.                                SPO_HS_OFF, -1) - line;
  1288.  
  1289.                 /* Compute the region start. */
  1290.                 /* Default is to use the end of the match. */
  1291.                 if (spp->sp_off_flags & (1 << SPO_RS_OFF))
  1292.                 eos_col = (spp->sp_prog->startp[0] - line)
  1293.                         + spp->sp_offsets[SPO_RS_OFF] - 1;
  1294.                 else
  1295.                 eos_col = (endp - line) - 1
  1296.                         + spp->sp_offsets[SPO_RS_OFF];
  1297.  
  1298.                 /*
  1299.                  * If this is a oneline, the end must be found
  1300.                  * in the same line too.
  1301.                  */
  1302.                 flags = 0;
  1303.                 eoep = NULL;
  1304.                 end_idx = 0;
  1305.                 if (spp->sp_type == SPTYPE_START
  1306.                     && (spp->sp_flags & HL_ONELINE))
  1307.                 endp = find_endp(idx, endp, endp == line,
  1308.                     &hl_endp, &flags, &eoep, &end_idx);
  1309.  
  1310.                 /*
  1311.                  * For a "match" the size must be > 0 after the
  1312.                  * end offset needs has been added.  Except when
  1313.                  * syncing.
  1314.                  */
  1315.                 else if (spp->sp_type == SPTYPE_MATCH)
  1316.                 {
  1317.                 hl_endp = syn_add_end_off(spp, SPO_HE_OFF, 0);
  1318.                 endp = syn_add_end_off(spp, SPO_ME_OFF, 0);
  1319.                 if (endp + syncing <= line + startcol)
  1320.                 {
  1321.                     /*
  1322.                      * If an empty string is matched, may need
  1323.                      * to try matching again at next column.
  1324.                      */
  1325.                     if (spp->sp_prog->startp[0] ==
  1326.                             spp->sp_prog->endp[0])
  1327.                     try_next_column = TRUE;
  1328.                     continue;
  1329.                 }
  1330.                 }
  1331.  
  1332.                 /* keep the best match so far in next_match_* */
  1333.                 if (endp != NULL)
  1334.                 {
  1335.                 if (hl_startcol < startcol)
  1336.                     hl_startcol = startcol;
  1337.                 if (hl_endp == NULL || hl_endp > endp)
  1338.                     hl_endp = endp;
  1339.  
  1340.                 next_match_idx = idx;
  1341.                 next_match_col = startcol;
  1342.                 next_match_m_endcol = endp - line;
  1343.                 next_match_h_endcol = hl_endp - line;
  1344.                 next_match_h_startcol = hl_startcol;
  1345.                 next_match_flags = flags;
  1346.                 next_match_eos_col = eos_col;
  1347.                 next_match_eoe_col = eoep - line;
  1348.                 next_match_end_idx = end_idx;
  1349.                 }
  1350.             }
  1351.             }
  1352.         }
  1353.  
  1354.         /*
  1355.          * If we found a match at the current column, use it.
  1356.          */
  1357.         if (next_match_idx >= 0 && next_match_col == (int)current_col)
  1358.         {
  1359.             cur_si = push_next_match(cur_si, line);
  1360.             found_match = TRUE;
  1361.         }
  1362.         }
  1363.     }
  1364.  
  1365.     /*
  1366.      * Handle searching for nextgroup match.
  1367.      */
  1368.     if (current_next_list)
  1369.     {
  1370.         /*
  1371.          * If a nextgroup was not found, continue looking for one if:
  1372.          * - this is an empty line and the "skipempty" option was given
  1373.          * - we are on white space and the "skipwhite" option was given
  1374.          */
  1375.         if (!found_match
  1376.             && (   ((current_next_flags & HL_SKIPWHITE)
  1377.                 && vim_iswhite(line[current_col]))
  1378.             || ((current_next_flags & HL_SKIPEMPTY)
  1379.                 && *line == NUL)))
  1380.         break;
  1381.  
  1382.         /*
  1383.          * If a nextgroup was found: Use it, and continue looking for
  1384.          * contained matches.
  1385.          * If a nextgroup was not found: Continue looking for a normal
  1386.          * match.
  1387.          */
  1388.         current_next_list = NULL;
  1389.         next_match_idx = -1;
  1390.         found_match = TRUE;
  1391.     }
  1392.  
  1393.     } while (found_match);
  1394.  
  1395.     /*
  1396.      * Use attributes from the current state, if within its highlighting.
  1397.      * If not, use attributes from the current-but-one state, etc.
  1398.      */
  1399.     current_attr = 0;
  1400.     current_id = 0;
  1401.     current_trans_id = 0;
  1402.     if (cur_si != NULL)
  1403.     {
  1404.     for (idx = current_state.ga_len - 1; idx >= 0; --idx)
  1405.     {
  1406.         sip = &CUR_STATE(idx);
  1407.         if ((int)current_col >= sip->si_h_startcol
  1408.                       && (int)current_col <= sip->si_h_endcol)
  1409.         {
  1410.         current_attr = sip->si_attr;
  1411.         current_id = sip->si_id;
  1412.         current_trans_id = sip->si_trans_id;
  1413.         break;
  1414.         }
  1415.     }
  1416.  
  1417.     /*
  1418.      * Check for end of current state (and the states before it) at the
  1419.      * next column.  Don't do this for syncing, because we would miss a
  1420.      * single character match.
  1421.      */
  1422.     if (!syncing)
  1423.     {
  1424.         ++current_col;
  1425.         check_state_ends(line);
  1426.         --current_col;
  1427.     }
  1428.     }
  1429.     
  1430.     /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
  1431.     if (current_next_list != NULL
  1432.         && line[current_col + 1] == NUL
  1433.         && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
  1434.     current_next_list = NULL;
  1435.  
  1436.     return current_attr;
  1437. }
  1438.  
  1439. /*
  1440.  * Check if we already matched pattern "idx" at the current column.
  1441.  */
  1442.     static int
  1443. did_match_already(idx)
  1444.     int        idx;
  1445. {
  1446.     int        i;
  1447.  
  1448.     for (i = current_state.ga_len; --i >= 0; )
  1449.     {
  1450.     if (CUR_STATE(i).si_m_startcol == (int)current_col
  1451.         && CUR_STATE(i).si_m_lnum == (int)current_lnum
  1452.         && CUR_STATE(i).si_idx == idx)
  1453.         return TRUE;
  1454.     }
  1455.     return FALSE;
  1456. }
  1457.  
  1458. /*
  1459.  * Push the next match onto the stack.
  1460.  */
  1461.     static struct state_item *
  1462. push_next_match(cur_si, line)
  1463.     struct state_item    *cur_si;
  1464.     char_u        *line;
  1465. {
  1466.     struct syn_pattern    *spp;
  1467.  
  1468.     spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
  1469.  
  1470.     /*
  1471.      * Push the item in current_state stack;
  1472.      */
  1473.     if (push_current(next_match_idx) == OK)
  1474.     {
  1475.     /*
  1476.      * If it's a start-skip-end type that crosses lines, figure out how
  1477.      * much it continues in this line.  Otherwise just fill in the length.
  1478.      */
  1479.     cur_si = &CUR_STATE(current_state.ga_len - 1);
  1480.     cur_si->si_h_startcol = next_match_h_startcol;
  1481.     cur_si->si_m_startcol = current_col;
  1482.     cur_si->si_m_lnum = current_lnum;
  1483.     cur_si->si_flags = spp->sp_flags;
  1484.     cur_si->si_next_list = spp->sp_next_list;
  1485.     if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
  1486.     {
  1487.         update_si_end(cur_si, line, next_match_m_endcol);
  1488.     }
  1489.     else
  1490.     {
  1491.         cur_si->si_m_endcol = next_match_m_endcol - 1;
  1492.         cur_si->si_h_endcol = next_match_h_endcol - 1;
  1493.         cur_si->si_ends = TRUE;
  1494.         cur_si->si_flags |= next_match_flags;
  1495.         cur_si->si_eoe_col = next_match_eoe_col;
  1496.         cur_si->si_end_idx = next_match_end_idx;
  1497.     }
  1498.     check_keepend();
  1499.     update_si_attr(current_state.ga_len - 1);
  1500.  
  1501.     /*
  1502.      * If the start pattern has another highlight group, push another item
  1503.      * on the stack for the start pattern.
  1504.      */
  1505.     if (       spp->sp_type == SPTYPE_START
  1506.         && spp->sp_syn_match_id != 0
  1507.         && spp->sp_syn_id != spp->sp_syn_match_id
  1508.         && push_current(next_match_idx) == OK)
  1509.     {
  1510.         cur_si = &CUR_STATE(current_state.ga_len - 1);
  1511.         cur_si->si_h_startcol = next_match_h_startcol;
  1512.         cur_si->si_m_startcol = current_col;
  1513.         cur_si->si_m_lnum = current_lnum;
  1514.         cur_si->si_m_endcol = next_match_eos_col;
  1515.         cur_si->si_h_endcol = next_match_eos_col;
  1516.         cur_si->si_ends = TRUE;
  1517.         cur_si->si_end_idx = 0;
  1518.         cur_si->si_flags = HL_MATCH;
  1519.         cur_si->si_next_list = NULL;
  1520.         check_keepend();
  1521.         update_si_attr(current_state.ga_len - 1);
  1522.     }
  1523.     }
  1524.  
  1525.     next_match_idx = -1;    /* try other match next time */
  1526.  
  1527.     return cur_si;
  1528. }
  1529.  
  1530. /*
  1531.  * Check for end of current state (and the states before it).
  1532.  */
  1533.     static void
  1534. check_state_ends(line)
  1535.     char_u        *line;
  1536. {
  1537.     struct state_item    *cur_si;
  1538.  
  1539.     cur_si = &CUR_STATE(current_state.ga_len - 1);
  1540.     for (;;)
  1541.     {
  1542.     if (cur_si->si_m_endcol < (int)current_col && cur_si->si_ends)
  1543.     {
  1544.         /*
  1545.          * If there is an end pattern group ID, highlight the end pattern
  1546.          * now.  No need to pop the current item from the stack.
  1547.          * Only do this if the end pattern continuous beyond the current
  1548.          * position.
  1549.          */
  1550.         if (cur_si->si_end_idx && cur_si->si_eoe_col >= (int)current_col)
  1551.         {
  1552.         cur_si->si_idx = cur_si->si_end_idx;
  1553.         cur_si->si_end_idx = 0;
  1554.         cur_si->si_m_endcol = cur_si->si_eoe_col;
  1555.         cur_si->si_h_endcol = cur_si->si_eoe_col;
  1556.         cur_si->si_flags |= HL_MATCH;
  1557.         update_si_attr(current_state.ga_len - 1);
  1558.         break;
  1559.         }
  1560.         else
  1561.         {
  1562.         /* handle next_list, unless at end of line and no "skipnl" or
  1563.          * "skipempty" */
  1564.         current_next_list = cur_si->si_next_list;
  1565.         current_next_flags = cur_si->si_flags;
  1566.         if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
  1567.             && line[current_col] == NUL)
  1568.             current_next_list = NULL;
  1569.         pop_current();
  1570.  
  1571.         if (current_state.ga_len == 0)
  1572.             break;
  1573.         cur_si = &CUR_STATE(current_state.ga_len - 1);
  1574.  
  1575.         /*
  1576.          * Only for a region the search for the end continues after
  1577.          * the end of the contained item.  If the contained match
  1578.          * included the end-of-line, break here, the region continues.
  1579.          * Don't do this when "keepend" is used.
  1580.          */
  1581.         if (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type == SPTYPE_START
  1582.                  && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
  1583.         {
  1584.             update_si_end(cur_si, line, (int)current_col);
  1585.             if (current_next_flags & HL_HAS_EOL)
  1586.             break;
  1587.         }
  1588.         }
  1589.     }
  1590.     else
  1591.         break;
  1592.     }
  1593. }
  1594.  
  1595. /*
  1596.  * Update an entry in the current_state stack for a match or region.  This
  1597.  * fills in si_attr and si_cont_list.
  1598.  */
  1599.     static void
  1600. update_si_attr(idx)
  1601.     int        idx;
  1602. {
  1603.     struct state_item    *sip = &CUR_STATE(idx);
  1604.     struct syn_pattern    *spp;
  1605.  
  1606.     spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
  1607.     if (sip->si_flags & HL_MATCH)
  1608.     sip->si_id = spp->sp_syn_match_id;
  1609.     else
  1610.     sip->si_id = spp->sp_syn_id;
  1611.     sip->si_attr = syn_id2attr(sip->si_id);
  1612.     sip->si_trans_id = sip->si_id;
  1613.     if (sip->si_flags & HL_MATCH)
  1614.     sip->si_cont_list = NULL;
  1615.     else
  1616.     sip->si_cont_list = spp->sp_cont_list;
  1617.  
  1618.     /*
  1619.      * For transparent items, take attr from outer item.
  1620.      * Also take cont_list, if there is none.
  1621.      * Don't do this for the matchgroup of a start or end pattern.
  1622.      */
  1623.     if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
  1624.     {
  1625.     if (idx == 0)
  1626.     {
  1627.         sip->si_attr = 0;
  1628.         sip->si_trans_id = 0;
  1629.         if (sip->si_cont_list == NULL)
  1630.         sip->si_cont_list = ID_LIST_ALL;
  1631.     }
  1632.     else
  1633.     {
  1634.         sip->si_attr = CUR_STATE(idx - 1).si_attr;
  1635.         sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
  1636.         if (sip->si_cont_list == NULL)
  1637.         sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
  1638.     }
  1639.     }
  1640. }
  1641.  
  1642. /*
  1643.  * Check the current stack for patterns with "keepend" flag.  Propagate the
  1644.  * match-end to contained items.
  1645.  */
  1646.     static void
  1647. check_keepend()
  1648. {
  1649.     int            i;
  1650.     int            maxend = MAXCOL;
  1651.     struct state_item    *sip;
  1652.  
  1653.     for (i = 0; i < current_state.ga_len; ++i)
  1654.     {
  1655.     sip = &CUR_STATE(i);
  1656.     if (maxend < MAXCOL)
  1657.     {
  1658.         if (sip->si_m_endcol > maxend)
  1659.         sip->si_m_endcol = maxend;
  1660.         if (sip->si_h_endcol > maxend)
  1661.         sip->si_h_endcol = maxend;
  1662.         if (sip->si_eoe_col > maxend)
  1663.         sip->si_eoe_col = maxend;
  1664.         sip->si_ends = TRUE;
  1665.     }
  1666.     if (sip->si_ends && (sip->si_flags & HL_KEEPEND)
  1667.                          && maxend > sip->si_m_endcol)
  1668.         maxend = sip->si_m_endcol;
  1669.     }
  1670. }
  1671.  
  1672. /*
  1673.  * Update an entry in the current_state stack for a start-skip-end pattern.
  1674.  * This finds the end of the current item, if it's in the current line.
  1675.  *
  1676.  * Return the flags for the matched END.
  1677.  */
  1678.     static void
  1679. update_si_end(sip, line, startcol)
  1680.     struct state_item    *sip;
  1681.     char_u        *line;
  1682.     int            startcol;   /* where to start searching for the end */
  1683. {
  1684.     char_u        *endp;
  1685.     char_u        *hl_endp;
  1686.     char_u        *end_endp;
  1687.     int            end_idx;
  1688.  
  1689.     /*
  1690.      * We need to find the end of the match.  It may continue in the next
  1691.      * line.
  1692.      */
  1693.     end_idx = 0;
  1694.     endp = find_endp(sip->si_idx, line + startcol,
  1695.            startcol == 0, &hl_endp, &(sip->si_flags), &end_endp, &end_idx);
  1696.     if (endp == NULL)
  1697.     {
  1698.     /* continues on next line */
  1699.     if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
  1700.     {
  1701.         sip->si_ends = TRUE;
  1702.         sip->si_m_endcol = STRLEN(line) - 1;
  1703.     }
  1704.     else
  1705.     {
  1706.         sip->si_ends = FALSE;
  1707.         sip->si_m_endcol = MAXCOL;
  1708.     }
  1709.     sip->si_h_endcol = sip->si_m_endcol;
  1710.     }
  1711.     else
  1712.     {
  1713.     /* match within this line */
  1714.     sip->si_m_endcol = endp - line - 1;
  1715.     sip->si_h_endcol = hl_endp - line - 1;
  1716.     sip->si_ends = TRUE;
  1717.     sip->si_eoe_col = end_endp - line - 1;
  1718.     sip->si_end_idx = end_idx;
  1719.     }
  1720.     check_keepend();
  1721. }
  1722.  
  1723. /*
  1724.  * Add a new state to the current state stack.
  1725.  * Return FAIL if it's not possible (out of memory).
  1726.  */
  1727.     static int
  1728. push_current(idx)
  1729.     int        idx;
  1730. {
  1731.     if (ga_grow(¤t_state, 1) == FAIL)
  1732.     return FAIL;
  1733.     vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(struct state_item));
  1734.     CUR_STATE(current_state.ga_len).si_idx = idx;
  1735.     ++current_state.ga_len;
  1736.     --current_state.ga_room;
  1737.     return OK;
  1738. }
  1739.  
  1740. /*
  1741.  * Remove a state from the current_state stack.
  1742.  */
  1743.     static void
  1744. pop_current()
  1745. {
  1746.     if (current_state.ga_len)
  1747.     {
  1748.     --current_state.ga_len;
  1749.     ++current_state.ga_room;
  1750.     }
  1751.     /* after the end of a pattern, try matching a keyword or pattern */
  1752.     next_match_idx = -1;
  1753. }
  1754.  
  1755. /*
  1756.  * Find the end of a start/skip/end pattern match.
  1757.  */
  1758.     static char_u *
  1759. find_endp(idx, sstart, at_bol, hl_endp, flagsp, end_endp, end_idx)
  1760.     int        idx;    /* index of the pattern */
  1761.     char_u  *sstart;    /* where to start looking for an END match */
  1762.     int        at_bol;    /* if sstart is at begin-of-line */
  1763.     char_u  **hl_endp;    /* end column for highlighting */
  1764.     int        *flagsp;    /* flags of matching END */
  1765.     char_u  **end_endp;    /* end of end pattern match */
  1766.     int        *end_idx;    /* group ID for end pattern match, or 0 */
  1767. {
  1768.     char_u        *endp;            /* end of highlighting */
  1769.     struct syn_pattern    *spp, *spp_skip;
  1770.     char_u        *p;            /* end of match */
  1771.     int            start_idx;
  1772.     int            best_idx;
  1773.     char_u        *best_ptr;
  1774.  
  1775.     /*
  1776.      * Check for being called with a START pattern.
  1777.      * Can happen with a match that continues to the next line, because it
  1778.      * contained a region.
  1779.      */
  1780.     spp = &(SYN_ITEMS(syn_buf)[idx]);
  1781.     if (spp->sp_type != SPTYPE_START)
  1782.     {
  1783.     *hl_endp = sstart;
  1784.     return sstart;
  1785.     }
  1786.  
  1787.     /*
  1788.      * Find the SKIP or first END pattern after the last START pattern.
  1789.      */
  1790.     for (;;)
  1791.     {
  1792.     spp = &(SYN_ITEMS(syn_buf)[idx]);
  1793.     if (spp->sp_type != SPTYPE_START)
  1794.         break;
  1795.     ++idx;
  1796.     }
  1797.  
  1798.     /*
  1799.      *    Lookup the SKIP pattern (if present)
  1800.      */
  1801.     if (spp->sp_type == SPTYPE_SKIP)
  1802.     {
  1803.     spp_skip = spp;
  1804.     ++idx;
  1805.     }
  1806.     else
  1807.     spp_skip = NULL;
  1808.  
  1809.     endp = sstart;        /* start looking for a match at sstart */
  1810.     start_idx = idx;        /* remember the first END pattern. */
  1811.     for (;;)
  1812.     {
  1813.     best_idx = -1;
  1814.     best_ptr = NULL;
  1815.     for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
  1816.     {
  1817.         spp = &(SYN_ITEMS(syn_buf)[idx]);
  1818.         if (spp->sp_type != SPTYPE_END)    /* past last END pattern */
  1819.         break;
  1820.  
  1821.         reg_ic = spp->sp_ic;
  1822.         if (vim_regexec(spp->sp_prog, endp, (at_bol && endp == sstart)))
  1823.         {
  1824.         if (best_idx == -1 || spp->sp_prog->startp[0] < best_ptr)
  1825.         {
  1826.             best_idx = idx;
  1827.             best_ptr = spp->sp_prog->startp[0];
  1828.         }
  1829.         }
  1830.     }
  1831.  
  1832.     /*
  1833.      * If all end patterns have been tried, and there is no match, the
  1834.      * item continues until end-of-line.
  1835.      */
  1836.     if (best_idx == -1)
  1837.         break;
  1838.  
  1839.     /*
  1840.      * If the skip pattern matches before the end pattern,
  1841.      * continue searching after the skip pattern.
  1842.      */
  1843.     if (       spp_skip != NULL
  1844.         && (reg_ic = spp_skip->sp_ic,
  1845.             vim_regexec(spp_skip->sp_prog, endp,
  1846.                           (at_bol && endp == sstart)))
  1847.         && spp_skip->sp_prog->startp[0] <= best_ptr)
  1848.     {
  1849.         /* Add offset to skip pattern match */
  1850.         p = syn_add_end_off(spp_skip, SPO_ME_OFF, 1);
  1851.  
  1852.         /* take care of an empty match or negative offset */
  1853.         if (p <= endp)
  1854.         ++endp;
  1855.         else if (p <= spp_skip->sp_prog->endp[0])
  1856.         endp = p;
  1857.         else
  1858.         /* Be careful not to jump over the NUL at the end-of-line */
  1859.         for (endp = spp_skip->sp_prog->endp[0];
  1860.                          *endp != NUL && endp < p; ++endp)
  1861.             ;
  1862.  
  1863.         /* if skip pattern includes end-of-line, return here */
  1864.         if (*endp == NUL)
  1865.         break;
  1866.  
  1867.         continue;        /* start with first end pattern again */
  1868.     }
  1869.  
  1870.     /*
  1871.      * Match from start pattern to end pattern.
  1872.      * Correct for match and highlight offset of end pattern.
  1873.      */
  1874.     spp = &(SYN_ITEMS(syn_buf)[best_idx]);
  1875.     p = syn_add_end_off(spp, SPO_ME_OFF, 1);
  1876.     if (p < sstart)
  1877.         p = sstart;
  1878.  
  1879.     endp = syn_add_end_off(spp, SPO_HE_OFF, 1);
  1880.     if (endp < sstart)
  1881.         endp = sstart;
  1882.     if (endp > p)
  1883.         endp = p;
  1884.     *end_endp = endp;
  1885.  
  1886.     /*
  1887.      * If the end group is highlighted differently, adjust the pointers.
  1888.      */
  1889.     if (spp->sp_syn_match_id != spp->sp_syn_id && spp->sp_syn_match_id != 0)
  1890.     {
  1891.         *end_idx = best_idx;
  1892.         if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
  1893.         endp = spp->sp_prog->endp[0] + spp->sp_offsets[SPO_RE_OFF];
  1894.         else
  1895.         endp = spp->sp_prog->startp[0] + spp->sp_offsets[SPO_RE_OFF];
  1896.         if (endp < sstart)
  1897.         endp = sstart;
  1898.         if (endp > p)
  1899.         endp = p;
  1900.         p = endp;
  1901.     }
  1902.     else
  1903.         *end_idx = 0;
  1904.     *hl_endp = endp;
  1905.     *flagsp = spp->sp_flags;
  1906.  
  1907.     return p;
  1908.     }
  1909.  
  1910.     return NULL;    /* no match for an END pattern in this line */
  1911. }
  1912.  
  1913. /*
  1914.  * Add offset to matched text for end of match or highlight.
  1915.  */
  1916.     static char_u *
  1917. syn_add_end_off(spp, idx, extra)
  1918.     struct syn_pattern    *spp;
  1919.     int            idx;
  1920.     int            extra;        /* extra chars for offset to start */
  1921. {
  1922.     if (spp->sp_off_flags & (1 << idx))
  1923.     return spp->sp_prog->startp[0] + spp->sp_offsets[idx] + extra;
  1924.     return spp->sp_prog->endp[0] + spp->sp_offsets[idx];
  1925. }
  1926.  
  1927. /*
  1928.  * Add offset to matched text for start of match or highlight.
  1929.  */
  1930.     static char_u *
  1931. syn_add_start_off(spp, idx, extra)
  1932.     struct syn_pattern    *spp;
  1933.     int            idx;
  1934.     int            extra;        /* extra chars for offset to end */
  1935. {
  1936.     if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
  1937.     return spp->sp_prog->endp[0] + spp->sp_offsets[idx] + extra;
  1938.     return spp->sp_prog->startp[0] + spp->sp_offsets[idx];
  1939. }
  1940.  
  1941. /*
  1942.  * Check one position in a line for a matching keyword.
  1943.  * The caller must check if a keyword can start at startcol.
  1944.  * Return it's ID if found, 0 otherwise.
  1945.  */
  1946.     static int
  1947. check_keyword_id(line, startcol, endcol, flags, next_list, cur_si)
  1948.     char_u        *line;
  1949.     int            startcol;   /* position in line to check for keyword */
  1950.     int            *endcol;    /* last character of found keyword */
  1951.     int            *flags;        /* flags of matching keyword */
  1952.     short        **next_list; /* next_list of matching keyword */
  1953.     struct state_item    *cur_si;    /* item at the top of the stack */
  1954. {
  1955.     struct keyentry *ktab;
  1956.     char_u        *p;
  1957.     int            round;
  1958.     int            hash;
  1959.     int            len;
  1960.     char_u        keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
  1961.  
  1962.     /* Find first character after the keyword */
  1963.     p = line + startcol;
  1964.     for (len = 1; vim_iswordc_buf(p[len], syn_buf); ++len)
  1965.     ;
  1966.     if (len > MAXKEYWLEN)
  1967.     return 0;
  1968.  
  1969.     /*
  1970.      * Must make a copy of the keyword, so we can add a NUL and make it
  1971.      * uppercase.
  1972.      */
  1973.     STRNCPY(keyword, p, len);
  1974.     keyword[len] = NUL;
  1975.  
  1976.     /*
  1977.      * Try twice:
  1978.      * 1. matching case
  1979.      * 2. ignoring case
  1980.      */
  1981.     for (round = 1; round <= 2; ++round)
  1982.     {
  1983.     if ((round == 1 ? syn_buf->b_keywtab : syn_buf->b_keywtab_ic) == NULL)
  1984.         continue;
  1985.     p = keyword;
  1986.     hash = 0;
  1987.     if (round == 1)    /* match case */
  1988.     {
  1989.         while (*p)
  1990.         hash += *p++;
  1991.         ktab = syn_buf->b_keywtab[hash & KHASH_MASK];
  1992.     }
  1993.     else /* round == 2, ignore case */
  1994.     {
  1995.         while (*p)
  1996.         {
  1997.         hash += (*p = TO_LOWER(*p));
  1998.         ++p;
  1999.         }
  2000.         ktab = syn_buf->b_keywtab_ic[hash & KHASH_MASK];
  2001.     }
  2002.  
  2003.     /*
  2004.      * Find keywords that match.
  2005.      * When current_next_list is non-zero accept only that group, otherwise:
  2006.      *  Accept a not-contained keyword at toplevel.
  2007.      *  Accept a keyword at other levels only if it is in the contains list.
  2008.      */
  2009.     for ( ; ktab != NULL; ktab = ktab->next)
  2010.         if (   STRCMP(keyword, ktab->keyword) == 0
  2011.         && (   (current_next_list != 0
  2012.             && in_id_list(current_next_list, ktab->syn_id, 0))
  2013.             || (current_next_list == 0
  2014.             && ((cur_si == NULL && !(ktab->flags & HL_CONTAINED))
  2015.                 || (cur_si != NULL
  2016.                 && in_id_list(cur_si->si_cont_list,
  2017.                     ktab->syn_id,
  2018.                     ktab->flags & HL_CONTAINED))))))
  2019.         {
  2020.         *endcol = startcol + len - 1;
  2021.         *flags = ktab->flags;
  2022.         *next_list = ktab->next_list;
  2023.         return ktab->syn_id;
  2024.         }
  2025.     }
  2026.     return 0;
  2027. }
  2028.  
  2029. /*
  2030.  * Handle ":syntax case" command.
  2031.  */
  2032. /* ARGSUSED */
  2033.     static void
  2034. syn_cmd_case(eap, syncing)
  2035.     EXARG    *eap;
  2036.     int        syncing;        /* not used */
  2037. {
  2038.     char_u    *arg = eap->arg;
  2039.     char_u    *next;
  2040.  
  2041.     eap->nextcmd = find_nextcmd(arg);
  2042.     if (eap->skip)
  2043.     return;
  2044.  
  2045.     next = skiptowhite(arg);
  2046.     if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
  2047.     curbuf->b_syn_ic = FALSE;
  2048.     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
  2049.     curbuf->b_syn_ic = TRUE;
  2050.     else
  2051.     EMSG2("Illegal argument: %s", arg);
  2052. }
  2053.  
  2054. /*
  2055.  * Clear all syntax info for one buffer.
  2056.  */
  2057.     void
  2058. syntax_clear(buf)
  2059.     BUF        *buf;
  2060. {
  2061.     int i;
  2062.  
  2063.     curbuf->b_syn_ic = FALSE;        /* Use case, by default */
  2064.  
  2065.     /* free the keywords */
  2066.     free_keywtab(buf->b_keywtab);
  2067.     buf->b_keywtab = NULL;
  2068.     free_keywtab(buf->b_keywtab_ic);
  2069.     buf->b_keywtab_ic = NULL;
  2070.  
  2071.     /* free the syntax patterns */
  2072.     for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
  2073.     syn_clear_pattern(buf, i);
  2074.     ga_clear(&buf->b_syn_patterns);
  2075.  
  2076.     buf->b_syn_sync_flags = 0;
  2077.     buf->b_syn_sync_minlines = 0;
  2078.     buf->b_syn_sync_maxlines = 0;
  2079.  
  2080.     vim_free(buf->b_syn_linecont_prog);
  2081.     buf->b_syn_linecont_prog = NULL;
  2082.     vim_free(buf->b_syn_linecont_pat);
  2083.     buf->b_syn_linecont_pat = NULL;
  2084.  
  2085.     /* free the stored states */
  2086.     syn_free_all_states(buf);
  2087. }
  2088.  
  2089. /*
  2090.  * Clear syncing info for one buffer.
  2091.  */
  2092.     static void
  2093. syntax_sync_clear()
  2094. {
  2095.     int i;
  2096.  
  2097.     /* free the syntax patterns */
  2098.     for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
  2099.     if (SYN_ITEMS(curbuf)[i].sp_syncing)
  2100.         syn_remove_pattern(curbuf, i);
  2101.  
  2102.     curbuf->b_syn_sync_flags = 0;
  2103.     curbuf->b_syn_sync_minlines = 0;
  2104.     curbuf->b_syn_sync_maxlines = 0;
  2105.  
  2106.     vim_free(curbuf->b_syn_linecont_prog);
  2107.     curbuf->b_syn_linecont_prog = NULL;
  2108.     vim_free(curbuf->b_syn_linecont_pat);
  2109.     curbuf->b_syn_linecont_pat = NULL;
  2110. }
  2111.  
  2112. /*
  2113.  * Remove one pattern from the buffer's pattern list.
  2114.  */
  2115.     static void
  2116. syn_remove_pattern(buf, idx)
  2117.     BUF        *buf;
  2118.     int        idx;
  2119. {
  2120.     struct syn_pattern    *spp;
  2121.  
  2122.     spp = &(SYN_ITEMS(buf)[idx]);
  2123.     syn_clear_pattern(buf, idx);
  2124.     vim_memmove(spp, spp + 1, sizeof(struct syn_pattern) *
  2125.                       (buf->b_syn_patterns.ga_len - idx - 1));
  2126.     --buf->b_syn_patterns.ga_len;
  2127.     --buf->b_syn_patterns.ga_room;
  2128. }
  2129.  
  2130. /*
  2131.  * Clear and free one syntax pattern.  When clearing all, must be called from
  2132.  * last to first!
  2133.  */
  2134.     static void
  2135. syn_clear_pattern(buf, i)
  2136.     BUF        *buf;
  2137.     int        i;
  2138. {
  2139.     vim_free(SYN_ITEMS(buf)[i].sp_pattern);
  2140.     vim_free(SYN_ITEMS(buf)[i].sp_prog);
  2141.     /* Only free sp_cont_list and sp_next_list of first start pattern */
  2142.     if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
  2143.     {
  2144.     vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
  2145.     vim_free(SYN_ITEMS(buf)[i].sp_next_list);
  2146.     }
  2147. }
  2148.  
  2149. /*
  2150.  * Handle ":syntax clear" command.
  2151.  */
  2152.     static void
  2153. syn_cmd_clear(eap, syncing)
  2154.     EXARG    *eap;
  2155.     int        syncing;
  2156. {
  2157.     char_u    *arg = eap->arg;
  2158.     char_u    *arg_end;
  2159.     int        id;
  2160.  
  2161.     eap->nextcmd = find_nextcmd(arg);
  2162.     if (eap->skip)
  2163.     return;
  2164.  
  2165.     if (ends_excmd(*arg))
  2166.     {
  2167.     /*
  2168.      * No argument: Clear all syntax items.
  2169.      */
  2170.     if (syncing)
  2171.         syntax_sync_clear();
  2172.     else
  2173.         syntax_clear(curbuf);
  2174.     }
  2175.     else
  2176.     {
  2177.     /*
  2178.      * Clear the group IDs that are in the argument.
  2179.      */
  2180.     while (!ends_excmd(*arg))
  2181.     {
  2182.         arg_end = skiptowhite(arg);
  2183.         id = syn_namen2id(arg, (int)(arg_end - arg));
  2184.         if (id == 0)
  2185.         {
  2186.         EMSG2("No such highlight group name: %s", arg);
  2187.         break;
  2188.         }
  2189.         else
  2190.         syn_clear_one(id, syncing);
  2191.         arg = skipwhite(arg_end);
  2192.     }
  2193.     }
  2194.     redraw_curbuf_later(NOT_VALID);
  2195. }
  2196.  
  2197. /*
  2198.  * Clear one syntax group for the current buffer.
  2199.  */
  2200.     static void
  2201. syn_clear_one(id, syncing)
  2202.     int        id;
  2203.     int        syncing;
  2204. {
  2205.     struct syn_pattern    *spp;
  2206.     int            idx;
  2207.  
  2208.     /* Clear keywords only when not ":syn sync clear group-name" */
  2209.     if (!syncing)
  2210.     {
  2211.     (void)syn_clear_keyword(id, curbuf->b_keywtab);
  2212.     (void)syn_clear_keyword(id, curbuf->b_keywtab_ic);
  2213.     }
  2214.  
  2215.     /* clear the patterns for "id" */
  2216.     for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
  2217.     {
  2218.     spp = &(SYN_ITEMS(curbuf)[idx]);
  2219.     if (spp->sp_syn_id != id || spp->sp_syncing != syncing)
  2220.         continue;
  2221.     syn_remove_pattern(curbuf, idx);
  2222.     }
  2223. }
  2224.  
  2225. /*
  2226.  * Handle ":syntax on" command.
  2227.  */
  2228. /* ARGSUSED */
  2229.     static void
  2230. syn_cmd_on(eap, syncing)
  2231.     EXARG    *eap;
  2232.     int        syncing;    /* not used */
  2233. {
  2234.     eap->nextcmd = check_nextcmd(eap->arg);
  2235.     if (!eap->skip)
  2236.     do_cmdline((char_u *)"so $VIM/syntax/syntax.vim",
  2237.                            NULL, NULL, DOCMD_VERBOSE);
  2238. }
  2239.  
  2240. /*
  2241.  * Handle ":syntax off" command.
  2242.  */
  2243. /* ARGSUSED */
  2244.     static void
  2245. syn_cmd_off(eap, syncing)
  2246.     EXARG    *eap;
  2247.     int        syncing;    /* not used */
  2248. {
  2249.     eap->nextcmd = check_nextcmd(eap->arg);
  2250.     if (!eap->skip)
  2251.     do_cmdline((char_u *)"so $VIM/syntax/nosyntax.vim",
  2252.                            NULL, NULL, DOCMD_VERBOSE);
  2253. }
  2254.  
  2255. /*
  2256.  * Handle ":syntax [list]" command: list current syntax words.
  2257.  */
  2258.     static void
  2259. syn_cmd_list(eap, syncing)
  2260.     EXARG    *eap;
  2261.     int        syncing;        /* when TRUE: list syncing items */
  2262. {
  2263.     char_u    *arg = eap->arg;
  2264.     int        id;
  2265.     char_u    *arg_end;
  2266.  
  2267.     eap->nextcmd = find_nextcmd(arg);
  2268.     if (eap->skip)
  2269.     return;
  2270.  
  2271.     if (!syntax_present(curbuf))
  2272.     {
  2273.     MSG("No Syntax items defined for this buffer");
  2274.     return;
  2275.     }
  2276.  
  2277.     if (syncing)
  2278.     {
  2279.     if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
  2280.     {
  2281.         MSG_PUTS("syncing on C-style comments");
  2282.         if (curbuf->b_syn_sync_minlines || curbuf->b_syn_sync_maxlines)
  2283.         syn_lines_msg();
  2284.         return;
  2285.     }
  2286.     else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
  2287.     {
  2288.         MSG_PUTS("syncing starts ");
  2289.         msg_outnum(curbuf->b_syn_sync_minlines);
  2290.         MSG_PUTS(" lines before top line");
  2291.         return;
  2292.     }
  2293.     MSG_PUTS_TITLE("\n--- Syntax sync items ---");
  2294.     if (curbuf->b_syn_sync_minlines || curbuf->b_syn_sync_maxlines)
  2295.     {
  2296.         MSG_PUTS("\nsyncing on items");
  2297.         syn_lines_msg();
  2298.     }
  2299.     }
  2300.     else
  2301.     MSG_PUTS_TITLE("\n--- Syntax items ---");
  2302.     if (ends_excmd(*arg))
  2303.     {
  2304.     /*
  2305.      * No argument: List all group IDs.
  2306.      */
  2307.     for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
  2308.         syn_list_one(id, syncing, FALSE);
  2309.     }
  2310.     else
  2311.     {
  2312.     /*
  2313.      * List the group IDs that are in the argument.
  2314.      */
  2315.     while (!ends_excmd(*arg) && !got_int)
  2316.     {
  2317.         arg_end = skiptowhite(arg);
  2318.         id = syn_namen2id(arg, (int)(arg_end - arg));
  2319.         if (id == 0)
  2320.         EMSG2("No such highlight group name: %s", arg);
  2321.         else
  2322.         syn_list_one(id, syncing, TRUE);
  2323.         arg = skipwhite(arg_end);
  2324.     }
  2325.     }
  2326.     eap->nextcmd = check_nextcmd(arg);
  2327. }
  2328.  
  2329.     static void
  2330. syn_lines_msg()
  2331. {
  2332.     MSG_PUTS("; ");
  2333.     if (curbuf->b_syn_sync_minlines)
  2334.     {
  2335.     MSG_PUTS("minimal ");
  2336.     msg_outnum(curbuf->b_syn_sync_minlines);
  2337.     if (curbuf->b_syn_sync_maxlines)
  2338.         MSG_PUTS(", ");
  2339.     }
  2340.     if (curbuf->b_syn_sync_maxlines)
  2341.     {
  2342.     MSG_PUTS("maximal ");
  2343.     msg_outnum(curbuf->b_syn_sync_maxlines);
  2344.     }
  2345.     MSG_PUTS(" lines before top line");
  2346. }
  2347.  
  2348. static int  last_matchgroup;
  2349.  
  2350. /*
  2351.  * List one syntax item, for ":syntax" or "syntax list syntax_name".
  2352.  */
  2353.     static void
  2354. syn_list_one(id, syncing, link_only)
  2355.     int        id;
  2356.     int        syncing;        /* when TRUE: list syncing items */
  2357.     int        link_only;        /* when TRUE; list link-only too */
  2358. {
  2359.     int            attr;
  2360.     int            idx;
  2361.     int            did_header = FALSE;
  2362.     struct syn_pattern    *spp;
  2363.  
  2364.     attr = highlight_attr[HLF_D];        /* highlight like directories */
  2365.  
  2366.     /* list the keywords for "id" */
  2367.     if (!syncing)
  2368.     {
  2369.     did_header = syn_list_keywords(id, curbuf->b_keywtab, FALSE, attr);
  2370.     did_header = syn_list_keywords(id, curbuf->b_keywtab_ic,
  2371.                                 did_header, attr);
  2372.     }
  2373.  
  2374.     /* list the patterns for "id" */
  2375.     for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
  2376.     {
  2377.     spp = &(SYN_ITEMS(curbuf)[idx]);
  2378.     if (spp->sp_syn_id != id || spp->sp_syncing != syncing)
  2379.         continue;
  2380.  
  2381.     (void)syn_list_header(did_header, 999, id);
  2382.     did_header = TRUE;
  2383.     last_matchgroup = 0;
  2384.     if (spp->sp_type == SPTYPE_MATCH)
  2385.     {
  2386.         put_pattern("match", ' ', spp, attr);
  2387.         msg_putchar(' ');
  2388.     }
  2389.     else if (spp->sp_type == SPTYPE_START)
  2390.     {
  2391.         while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
  2392.         put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
  2393.         if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
  2394.         put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
  2395.         while (idx < curbuf->b_syn_patterns.ga_len
  2396.                   && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
  2397.         put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
  2398.         --idx;
  2399.         msg_putchar(' ');
  2400.     }
  2401.     if (spp->sp_flags & HL_CONTAINED)
  2402.     {
  2403.         msg_puts_attr((char_u *)"contained", attr);
  2404.         msg_putchar(' ');
  2405.     }
  2406.     if (spp->sp_flags & HL_ONELINE)
  2407.     {
  2408.         msg_puts_attr((char_u *)"oneline", attr);
  2409.         msg_putchar(' ');
  2410.     }
  2411.     if (spp->sp_flags & HL_KEEPEND)
  2412.     {
  2413.         msg_puts_attr((char_u *)"keepend", attr);
  2414.         msg_putchar(' ');
  2415.     }
  2416.     if (spp->sp_flags & HL_TRANSP)
  2417.     {
  2418.         msg_puts_attr((char_u *)"transparent", attr);
  2419.         msg_putchar(' ');
  2420.     }
  2421.     if (spp->sp_cont_list != NULL)
  2422.     {
  2423.         put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
  2424.     }
  2425.     if (spp->sp_next_list != NULL)
  2426.     {
  2427.         put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
  2428.         if (spp->sp_flags & HL_SKIPWHITE)
  2429.         {
  2430.         msg_puts_attr((char_u *)"skipwhite", attr);
  2431.         msg_putchar(' ');
  2432.         }
  2433.         if (spp->sp_flags & HL_SKIPNL)
  2434.         {
  2435.         msg_puts_attr((char_u *)"skipnl", attr);
  2436.         msg_putchar(' ');
  2437.         }
  2438.         if (spp->sp_flags & HL_SKIPEMPTY)
  2439.         {
  2440.         msg_puts_attr((char_u *)"skipempty", attr);
  2441.         msg_putchar(' ');
  2442.         }
  2443.     }
  2444.     if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
  2445.     {
  2446.         if (spp->sp_flags & HL_SYNC_HERE)
  2447.         msg_puts_attr((char_u *)"grouphere", attr);
  2448.         else
  2449.         msg_puts_attr((char_u *)"groupthere", attr);
  2450.         msg_putchar(' ');
  2451.         if (spp->sp_sync_idx >= 0)
  2452.         msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
  2453.                    [spp->sp_sync_idx].sp_syn_id - 1].sg_name);
  2454.         else
  2455.         MSG_PUTS("NONE");
  2456.         msg_putchar(' ');
  2457.     }
  2458.     }
  2459.  
  2460.     /* list the link, if there is one */
  2461.     if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
  2462.     {
  2463.     (void)syn_list_header(did_header, 999, id);
  2464.     msg_puts_attr((char_u *)"links to", attr);
  2465.     msg_putchar(' ');
  2466.     msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
  2467.     }
  2468. }
  2469.  
  2470.     static void
  2471. put_id_list(name, list, attr)
  2472.     char_u    *name;
  2473.     short    *list;
  2474.     int        attr;
  2475. {
  2476.     short        *p;
  2477.  
  2478.     msg_puts_attr(name, attr);
  2479.     msg_putchar('=');
  2480.     for (p = list; *p; ++p)
  2481.     {
  2482.     if (*p == CONTAINS_ALLBUT)
  2483.     {
  2484.         if (p[1])
  2485.         MSG_PUTS("ALLBUT");
  2486.         else
  2487.         MSG_PUTS("ALL");
  2488.     }
  2489.     else
  2490.         msg_outtrans(HL_TABLE()[*p - 1].sg_name);
  2491.     if (p[1])
  2492.         msg_putchar(',');
  2493.     }
  2494.     msg_putchar(' ');
  2495. }
  2496.  
  2497.     static void
  2498. put_pattern(s, c, spp, attr)
  2499.     char        *s;
  2500.     int            c;
  2501.     struct syn_pattern    *spp;
  2502.     int            attr;
  2503. {
  2504.     long    n;
  2505.     int        mask;
  2506.     int        first;
  2507.     static char    *sepchars = "/+=-#@\"|'^&";
  2508.     int        i;
  2509.  
  2510.     /* May have to write "matchgroup=group" */
  2511.     if (last_matchgroup != spp->sp_syn_match_id)
  2512.     {
  2513.     last_matchgroup = spp->sp_syn_match_id;
  2514.     msg_puts_attr((char_u *)"matchgroup", attr);
  2515.     msg_putchar('=');
  2516.     if (last_matchgroup == 0)
  2517.         msg_outtrans((char_u *)"NONE");
  2518.     else
  2519.         msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
  2520.     msg_putchar(' ');
  2521.     }
  2522.  
  2523.     /* output the name of the pattern and an '=' or ' ' */
  2524.     msg_puts_attr((char_u *)s, attr);
  2525.     msg_putchar(c);
  2526.  
  2527.     /* output the pattern, in between a char that is not in the pattern */
  2528.     for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
  2529.     if (sepchars[++i] == NUL)
  2530.     {
  2531.         i = 0;    /* no good char found, just use the first one */
  2532.         break;
  2533.     }
  2534.     msg_putchar(sepchars[i]);
  2535.     msg_outtrans(spp->sp_pattern);
  2536.     msg_putchar(sepchars[i]);
  2537.  
  2538.     /* output any pattern options */
  2539.     first = TRUE;
  2540.     for (i = 0; i < SPO_COUNT; ++i)
  2541.     {
  2542.     mask = (1 << i);
  2543.     if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
  2544.     {
  2545.         if (!first)
  2546.         msg_putchar(',');    /* separate with commas */
  2547.         msg_puts((char_u *)spo_name_tab[i]);
  2548.         n = spp->sp_offsets[i];
  2549.         if (i != SPO_LC_OFF)
  2550.         {
  2551.         if (spp->sp_off_flags & mask)
  2552.             msg_putchar('s');
  2553.         else
  2554.             msg_putchar('e');
  2555.         if (n > 0)
  2556.             msg_putchar('+');
  2557.         }
  2558.         if (n || i == SPO_LC_OFF)
  2559.         msg_outnum(n);
  2560.         first = FALSE;
  2561.     }
  2562.     }
  2563.     msg_putchar(' ');
  2564. }
  2565.  
  2566. /*
  2567.  * List or clear the keywords for one syntax group.
  2568.  * Return TRUE if the header has been printed.
  2569.  */
  2570.     static int
  2571. syn_list_keywords(id, ktabp, did_header, attr)
  2572.     int            id;
  2573.     struct keyentry **ktabp;
  2574.     int            did_header;        /* header has already been printed */
  2575.     int            attr;
  2576. {
  2577.     int            i;
  2578.     int            outlen;
  2579.     struct keyentry *ktab;
  2580.     int            prev_contained = 0;
  2581.     short        *prev_next_list = NULL;
  2582.     int            prev_skipnl = 0;
  2583.     int            prev_skipwhite = 0;
  2584.     int            prev_skipempty = 0;
  2585.  
  2586.     if (ktabp == NULL)
  2587.     return did_header;
  2588.  
  2589.     /*
  2590.      * Unfortunately, this list of keywords is not sorted on alphabet but on
  2591.      * hash value...
  2592.      */
  2593.     for (i = 0; i < KHASH_SIZE; ++i)
  2594.     {
  2595.     for (ktab = ktabp[i]; ktab != NULL && !got_int; ktab = ktab->next)
  2596.     {
  2597.         if (ktab->syn_id == id)
  2598.         {
  2599.         if (prev_contained != (ktab->flags & HL_CONTAINED)
  2600.             || prev_skipnl != (ktab->flags & HL_SKIPNL)
  2601.             || prev_skipwhite != (ktab->flags & HL_SKIPWHITE)
  2602.             || prev_skipempty != (ktab->flags & HL_SKIPEMPTY)
  2603.             || prev_next_list != ktab->next_list)
  2604.             outlen = 9999;
  2605.         else
  2606.             outlen = STRLEN(ktab->keyword);
  2607.         /* output "contained" and "nextgroup" on each line */
  2608.         if (syn_list_header(did_header, outlen, id))
  2609.         {
  2610.             prev_contained = 0;
  2611.             prev_next_list = NULL;
  2612.             prev_skipnl = 0;
  2613.             prev_skipwhite = 0;
  2614.             prev_skipempty = 0;
  2615.         }
  2616.         did_header = TRUE;
  2617.         if (prev_contained != (ktab->flags & HL_CONTAINED))
  2618.         {
  2619.             msg_puts_attr((char_u *)"contained", attr);
  2620.             msg_putchar(' ');
  2621.             prev_contained = (ktab->flags & HL_CONTAINED);
  2622.         }
  2623.         if (ktab->next_list != prev_next_list)
  2624.         {
  2625.             put_id_list((char_u *)"nextgroup", ktab->next_list, attr);
  2626.             msg_putchar(' ');
  2627.             prev_next_list = ktab->next_list;
  2628.             if (ktab->flags & HL_SKIPNL)
  2629.             {
  2630.             msg_puts_attr((char_u *)"skipnl", attr);
  2631.             msg_putchar(' ');
  2632.             prev_skipnl = (ktab->flags & HL_SKIPNL);
  2633.             }
  2634.             if (ktab->flags & HL_SKIPWHITE)
  2635.             {
  2636.             msg_puts_attr((char_u *)"skipwhite", attr);
  2637.             msg_putchar(' ');
  2638.             prev_skipwhite = (ktab->flags & HL_SKIPWHITE);
  2639.             }
  2640.             if (ktab->flags & HL_SKIPEMPTY)
  2641.             {
  2642.             msg_puts_attr((char_u *)"skipempty", attr);
  2643.             msg_putchar(' ');
  2644.             prev_skipempty = (ktab->flags & HL_SKIPEMPTY);
  2645.             }
  2646.         }
  2647.         msg_outtrans(ktab->keyword);
  2648.         }
  2649.     }
  2650.     }
  2651.  
  2652.     return did_header;
  2653. }
  2654.  
  2655.     static void
  2656. syn_clear_keyword(id, ktabp)
  2657.     int            id;
  2658.     struct keyentry **ktabp;
  2659. {
  2660.     int            i;
  2661.     struct keyentry *ktab;
  2662.     struct keyentry *ktab_prev;
  2663.     struct keyentry *ktab_next;
  2664.  
  2665.     if (ktabp == NULL)        /* no keywords present */
  2666.     return;
  2667.  
  2668.     for (i = 0; i < KHASH_SIZE; ++i)
  2669.     {
  2670.     ktab_prev = NULL;
  2671.     for (ktab = ktabp[i]; ktab != NULL; )
  2672.     {
  2673.         if (ktab->syn_id == id)
  2674.         {
  2675.         ktab_next = ktab->next;
  2676.         if (ktab_prev == NULL)
  2677.             ktabp[i] = ktab_next;
  2678.         else
  2679.             ktab_prev->next = ktab_next;
  2680.         vim_free(ktab);
  2681.         ktab = ktab_next;
  2682.         }
  2683.         else
  2684.         {
  2685.         ktab_prev = ktab;
  2686.         ktab = ktab->next;
  2687.         }
  2688.     }
  2689.     }
  2690. }
  2691.  
  2692. /*
  2693.  * Recursive function to free() a branch of a kwordtab.
  2694.  */
  2695.     static void
  2696. free_keywtab(ktabp)
  2697.     struct keyentry **ktabp;
  2698. {
  2699.     int            i;
  2700.     struct keyentry *ktab;
  2701.     struct keyentry *ktab_next;
  2702.  
  2703.     if (ktabp != NULL)
  2704.     {
  2705.     for (i = 0; i < KHASH_SIZE; ++i)
  2706.         for (ktab = ktabp[i]; ktab != NULL; ktab = ktab_next)
  2707.         {
  2708.         ktab_next = ktab->next;
  2709.         vim_free(ktab);
  2710.         }
  2711.     vim_free(ktabp);
  2712.     }
  2713. }
  2714.  
  2715. /*
  2716.  * Add a keyword to the list of keywords.
  2717.  */
  2718.     static void
  2719. add_keyword(name, id, flags, next_list)
  2720.     char_u    *name;        /* name of keyword */
  2721.     int        id;        /* group ID for this keyword */
  2722.     int        flags;        /* flags for this keyword */
  2723.     short    *next_list; /* nextgroup for this keyword */
  2724. {
  2725.     struct keyentry    *ktab;
  2726.     struct keyentry    ***ktabpp;
  2727.     char_u        *p;
  2728.     int            hash;
  2729.  
  2730.     ktab = (struct keyentry *)alloc(
  2731.                    (int)(sizeof(struct keyentry) + STRLEN(name)));
  2732.     if (ktab == NULL)
  2733.     return;
  2734.     STRCPY(ktab->keyword, name);
  2735.     ktab->syn_id = id;
  2736.     ktab->flags = flags;
  2737.     ktab->next_list = copy_id_list(next_list);
  2738.  
  2739.     if (curbuf->b_syn_ic)
  2740.     {
  2741.     for (p = ktab->keyword; *p; ++p)
  2742.         *p = TO_LOWER(*p);
  2743.     ktabpp = &curbuf->b_keywtab_ic;
  2744.     }
  2745.     else
  2746.     ktabpp = &curbuf->b_keywtab;
  2747.  
  2748.     if (*ktabpp == NULL)
  2749.     {
  2750.     *ktabpp = (struct keyentry **)alloc_clear(
  2751.                    (int)(sizeof(struct keyentry *) * KHASH_SIZE));
  2752.     if (*ktabpp == NULL)
  2753.         return;
  2754.     }
  2755.  
  2756.     hash = 0;
  2757.     for (p = ktab->keyword; *p; ++p)
  2758.     hash += *p;
  2759.     hash &= KHASH_MASK;
  2760.  
  2761.     ktab->next = (*ktabpp)[hash];
  2762.     (*ktabpp)[hash] = ktab;
  2763. }
  2764.  
  2765. /*
  2766.  * Get the start and end of the group name argument.
  2767.  * Return a pointer to the first argument.
  2768.  * Return NULL if the end of the command was found instead of further args.
  2769.  */
  2770.     static char_u *
  2771. get_group_name(arg, name_end)
  2772.     char_u    *arg;        /* start of the argument */
  2773.     char_u    **name_end;    /* pointer to end of the name */
  2774. {
  2775.     char_u    *rest;
  2776.  
  2777.     *name_end = skiptowhite(arg);
  2778.     rest = skipwhite(*name_end);
  2779.  
  2780.     /*
  2781.      * Check if there are enough arguments.  The first argument may be a
  2782.      * pattern, where '|' is allowed, so only check for NUL.
  2783.      */
  2784.     if (ends_excmd(*arg) || *rest == NUL)
  2785.     return NULL;
  2786.     return rest;
  2787. }
  2788.  
  2789. /*
  2790.  * Check for syntax command option arguments.
  2791.  * This can be called at any place in the list of arguments, and just picks
  2792.  * out the arguments that are known.  Can be called several times in a row to
  2793.  * collect all options in between other arguments.
  2794.  * Return a pointer to the next argument (which isn't an option).
  2795.  * Return NULL for any error;
  2796.  */
  2797.     static char_u *
  2798. get_syn_options(arg, flagsp, sync_idx, cont_list, next_list)
  2799.     char_u    *arg;        /* next argument */
  2800.     int        *flagsp;    /* flags for contained and transpartent */
  2801.     int        *sync_idx;    /* syntax item for "grouphere" argument, NULL
  2802.                    if not allowed */
  2803.     short    **cont_list;    /* group IDs for "contains" argument, NULL if
  2804.                    not allowed */
  2805.     short    **next_list;    /* group IDs for "nextgroup" argument */
  2806. {
  2807.     int        flags;
  2808.     char_u    *gname_start, *gname;
  2809.     int        syn_id;
  2810.     int        len;
  2811.     int        i;
  2812.     int        fidx;
  2813.     static struct flag
  2814.     {
  2815.     char    *name;
  2816.     int    len;
  2817.     int    val;
  2818.     } flagtab[] = { {"contained",   9,    HL_CONTAINED},
  2819.             {"oneline",        7,    HL_ONELINE},
  2820.             {"keepend",        7,    HL_KEEPEND},
  2821.             {"transparent", 11, HL_TRANSP},
  2822.             {"skipnl",        6,    HL_SKIPNL},
  2823.             {"skipwhite",   9,    HL_SKIPWHITE},
  2824.             {"skipempty",   9,    HL_SKIPEMPTY},
  2825.             {"grouphere",   9,    HL_SYNC_HERE},
  2826.             {"groupthere",  10,    HL_SYNC_THERE},
  2827.         };
  2828.  
  2829.     if (arg == NULL)        /* already detected error */
  2830.     return NULL;
  2831.  
  2832.     flags = *flagsp;
  2833.     for (;;)
  2834.     {
  2835.     for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
  2836.     {
  2837.         len = flagtab[fidx].len;
  2838.         if (STRNICMP(arg, flagtab[fidx].name, len) == 0
  2839.             && (ends_excmd(arg[len]) || vim_iswhite(arg[len])))
  2840.         {
  2841.         flags |= flagtab[fidx].val;
  2842.         arg = skipwhite(arg + len);
  2843.  
  2844.         if (flagtab[fidx].val == HL_SYNC_HERE
  2845.             || flagtab[fidx].val == HL_SYNC_THERE)
  2846.         {
  2847.             if (sync_idx == NULL)
  2848.             {
  2849.             EMSG("group[t]here not accepted here");
  2850.             return NULL;
  2851.             }
  2852.  
  2853.             gname_start = arg;
  2854.             arg = skiptowhite(arg);
  2855.             if (gname_start == arg)
  2856.             return NULL;
  2857.             gname = vim_strnsave(gname_start, (int)(arg - gname_start));
  2858.             if (gname == NULL)
  2859.             return NULL;
  2860.             if (STRCMP(gname, "NONE") == 0)
  2861.             *sync_idx = NONE_IDX;
  2862.             else
  2863.             {
  2864.             syn_id = syn_name2id(gname);
  2865.             for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
  2866.                 if (SYN_ITEMS(curbuf)[i].sp_syn_id == syn_id &&
  2867.                  SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
  2868.                 {
  2869.                 *sync_idx = i;
  2870.                 break;
  2871.                 }
  2872.             if (i < 0)
  2873.             {
  2874.                 EMSG2("Didn't find region item for %s", gname);
  2875.                 vim_free(gname);
  2876.                 return NULL;
  2877.             }
  2878.             }
  2879.  
  2880.             vim_free(gname);
  2881.             arg = skipwhite(arg);
  2882.         }
  2883.         break;
  2884.         }
  2885.     }
  2886.     if (fidx >= 0)
  2887.         continue;
  2888.  
  2889.     if (STRNICMP(arg, "contains", 8) == 0
  2890.         && (vim_iswhite(arg[8]) || arg[8] == '='))
  2891.     {
  2892.         if (cont_list == NULL)
  2893.         {
  2894.         EMSG("contains argument not accepted here");
  2895.         return NULL;
  2896.         }
  2897.         if (get_id_list(&arg, 8, cont_list) == FAIL)
  2898.         return NULL;
  2899.     }
  2900.     else if (STRNICMP(arg, "nextgroup", 9) == 0
  2901.         && (vim_iswhite(arg[9]) || arg[9] == '='))
  2902.     {
  2903.         if (get_id_list(&arg, 9, next_list) == FAIL)
  2904.         return NULL;
  2905.     }
  2906.     else
  2907.         break;
  2908.     }
  2909.  
  2910.     *flagsp = flags;
  2911.  
  2912.     return arg;
  2913. }
  2914.  
  2915. /*
  2916.  * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
  2917.  */
  2918. /* ARGSUSED */
  2919.     static void
  2920. syn_cmd_keyword(eap, syncing)
  2921.     EXARG    *eap;
  2922.     int        syncing;        /* not used */
  2923. {
  2924.     char_u    *arg = eap->arg;
  2925.     char_u    *group_name_end;
  2926.     int        syn_id;
  2927.     char_u    *rest;
  2928.     char_u    *keyword_copy;
  2929.     char_u    *p;
  2930.     char_u    *first_arg;
  2931.     int        round;
  2932.     int        flags = 0;
  2933.     short    *next_list = NULL;
  2934.  
  2935.     rest = get_group_name(arg, &group_name_end);
  2936.  
  2937.     if (rest != NULL)
  2938.     {
  2939.     syn_id = syn_check_group(arg, (int)(group_name_end - arg));
  2940.  
  2941.     /* allocate a buffer, for removing the backslashes in the keyword */
  2942.     keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
  2943.     if (keyword_copy != NULL)
  2944.     {
  2945.         /*
  2946.          * The options given apply to ALL keywords, so all options must be
  2947.          * found before keywords can be created.
  2948.          * round 1: collect the options.
  2949.          * round 2: create the keywords.
  2950.          */
  2951.         first_arg = rest;
  2952.         for (round = 1; round <= 2; ++round)
  2953.         {
  2954.         /*
  2955.          * Isolate each keyword and add an entry for it.
  2956.          */
  2957.         for (rest = first_arg; rest != NULL && !ends_excmd(*rest);
  2958.                                rest = skipwhite(rest))
  2959.         {
  2960.             rest = get_syn_options(rest, &flags, NULL,
  2961.                                 NULL, &next_list);
  2962.             if (rest == NULL || ends_excmd(*rest))
  2963.             break;
  2964.             p = keyword_copy;
  2965.             while (*rest && !vim_iswhite(*rest))
  2966.             {
  2967.             if (*rest == '\\' && rest[1] != NUL)
  2968.                 ++rest;
  2969.             *p++ = *rest++;
  2970.             }
  2971.             *p = NUL;
  2972.             if (round == 2 && !eap->skip)
  2973.             {
  2974.             for (p = vim_strchr(keyword_copy, '['); ; ++p)
  2975.             {
  2976.                 if (p != NULL)
  2977.                 *p = NUL;
  2978.                 add_keyword(keyword_copy, syn_id, flags, next_list);
  2979.                 if (p == NULL || p[1] == NUL || p[1] == ']')
  2980.                 break;
  2981.                 p[0] = p[1];
  2982.             }
  2983.             }
  2984.         }
  2985.         }
  2986.         vim_free(keyword_copy);
  2987.     }
  2988.     }
  2989.  
  2990.     if (rest != NULL)
  2991.     eap->nextcmd = check_nextcmd(rest);
  2992.     else
  2993.     EMSG2(e_invarg2, arg);
  2994.  
  2995.     vim_free(next_list);
  2996.     redraw_curbuf_later(NOT_VALID);
  2997. }
  2998.  
  2999. /*
  3000.  * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
  3001.  *
  3002.  * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
  3003.  */
  3004.     static void
  3005. syn_cmd_match(eap, syncing)
  3006.     EXARG    *eap;
  3007.     int        syncing;        /* TRUE for ":syntax sync match .. " */
  3008. {
  3009.     char_u        *arg = eap->arg;
  3010.     char_u        *group_name_end;
  3011.     char_u        *rest;
  3012.     struct syn_pattern    item;        /* the item found in the line */
  3013.     int            syn_id;
  3014.     int            idx;
  3015.     int            flags = 0;
  3016.     int            sync_idx = 0;
  3017.     short        *cont_list = NULL;
  3018.     short        *next_list = NULL;
  3019.  
  3020.     /* Isolate the group name, check for validity */
  3021.     rest = get_group_name(arg, &group_name_end);
  3022.  
  3023.     /* Get options before the pattern */
  3024.     rest = get_syn_options(rest, &flags, syncing ? &sync_idx : NULL,
  3025.                               &cont_list, &next_list);
  3026.  
  3027.     /* get the pattern. */
  3028.     init_syn_patterns();
  3029.     vim_memset(&item, 0, sizeof(item));
  3030.     rest = get_syn_pattern(rest, &item);
  3031.     if (vim_regcomp_had_eol())
  3032.     flags |= HL_HAS_EOL;
  3033.  
  3034.     /* Get options after the pattern */
  3035.     rest = get_syn_options(rest, &flags, syncing ? &sync_idx : NULL,
  3036.                               &cont_list, &next_list);
  3037.  
  3038.     if (rest != NULL)        /* all arguments are valid */
  3039.     {
  3040.     /*
  3041.      * Check for trailing command and illegal trailing arguments.
  3042.      */
  3043.     eap->nextcmd = check_nextcmd(rest);
  3044.     if (!ends_excmd(*rest) || eap->skip)
  3045.         rest = NULL;
  3046.     else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
  3047.         && (syn_id = syn_check_group(arg,
  3048.                        (int)(group_name_end - arg))) != 0)
  3049.     {
  3050.         /*
  3051.          * Store the pattern in the syn_items list
  3052.          */
  3053.         idx = curbuf->b_syn_patterns.ga_len;
  3054.         SYN_ITEMS(curbuf)[idx] = item;
  3055.         SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
  3056.         SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
  3057.         SYN_ITEMS(curbuf)[idx].sp_syn_id = syn_id;
  3058.         SYN_ITEMS(curbuf)[idx].sp_flags = flags;
  3059.         SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
  3060.         SYN_ITEMS(curbuf)[idx].sp_cont_list = cont_list;
  3061.         SYN_ITEMS(curbuf)[idx].sp_next_list = next_list;
  3062.         ++curbuf->b_syn_patterns.ga_len;
  3063.         --curbuf->b_syn_patterns.ga_room;
  3064.  
  3065.         /* remember that we found a match for syncing on */
  3066.         if (flags & (HL_SYNC_HERE|HL_SYNC_THERE))
  3067.         curbuf->b_syn_sync_flags |= SF_MATCH;
  3068.  
  3069.         redraw_curbuf_later(NOT_VALID);
  3070.         return;    /* don't free the progs and patterns now */
  3071.     }
  3072.     }
  3073.  
  3074.     /*
  3075.      * Something failed, free the allocated memory.
  3076.      */
  3077.     vim_free(item.sp_prog);
  3078.     vim_free(item.sp_pattern);
  3079.     vim_free(cont_list);
  3080.     vim_free(next_list);
  3081.  
  3082.     if (rest == NULL)
  3083.     EMSG2(e_invarg2, arg);
  3084. }
  3085.  
  3086. /*
  3087.  * Handle ":syntax region {group-name} [matchgroup={group-name}]
  3088.  *        start {start} .. [skip {skip}] end {end} .. [{options}]".
  3089.  */
  3090.     static void
  3091. syn_cmd_region(eap, syncing)
  3092.     EXARG    *eap;
  3093.     int        syncing;        /* TRUE for ":syntax sync region .." */
  3094. {
  3095.     char_u        *arg = eap->arg;
  3096.     char_u        *group_name_end;
  3097.     char_u        *rest;            /* next arg, NULL on error */
  3098.     char_u        *key_end;
  3099.     char_u        *key = NULL;
  3100.     char_u        *p;
  3101.     int            item;
  3102. #define ITEM_START        0
  3103. #define ITEM_SKIP        1
  3104. #define ITEM_END        2
  3105. #define ITEM_MATCHGROUP        3
  3106.     struct pat_ptr
  3107.     {
  3108.     struct syn_pattern  *pp_synp;        /* pointer to syn_pattern */
  3109.     int            pp_matchgroup_id;    /* matchgroup ID */
  3110.     struct pat_ptr        *pp_next;        /* pointer to next pat_ptr */
  3111.     }            *(pat_ptrs[3]);
  3112.                         /* patterns found in the line */
  3113.     struct pat_ptr    *ppp;
  3114.     struct pat_ptr    *ppp_next;
  3115.     int            pat_count = 0;        /* number of syn_patterns found */
  3116.     int            syn_id;
  3117.     int            matchgroup_id = 0;
  3118.     int            not_enough = FALSE;    /* not enough arguments */
  3119.     int            illegal = FALSE;    /* illegal arguments */
  3120.     int            success = FALSE;
  3121.     int            idx;
  3122.     int            flags = 0;
  3123.     short        *cont_list = NULL;
  3124.     short        *next_list = NULL;
  3125.  
  3126.     /* Isolate the group name, check for validity */
  3127.     rest = get_group_name(arg, &group_name_end);
  3128.  
  3129.     pat_ptrs[0] = NULL;
  3130.     pat_ptrs[1] = NULL;
  3131.     pat_ptrs[2] = NULL;
  3132.  
  3133.     init_syn_patterns();
  3134.  
  3135.     /*
  3136.      * get the options, patterns and matchgroup.
  3137.      */
  3138.     while (rest != NULL && !ends_excmd(*rest))
  3139.     {
  3140.     /* Check for option arguments */
  3141.     rest = get_syn_options(rest, &flags, NULL, &cont_list, &next_list);
  3142.     if (rest == NULL || ends_excmd(*rest))
  3143.         break;
  3144.  
  3145.     /* must be a pattern or matchgroup then */
  3146.     key_end = rest;
  3147.     while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
  3148.         ++key_end;
  3149.     vim_free(key);
  3150.     key = vim_strnsave_up(rest, (int)(key_end - rest));
  3151.     if (key == NULL)            /* out of memory */
  3152.     {
  3153.         rest = NULL;
  3154.         break;
  3155.     }
  3156.     if (STRCMP(key, "MATCHGROUP") == 0)
  3157.         item = ITEM_MATCHGROUP;
  3158.     else if (STRCMP(key, "START") == 0)
  3159.         item = ITEM_START;
  3160.     else if (STRCMP(key, "END") == 0)
  3161.         item = ITEM_END;
  3162.     else if (STRCMP(key, "SKIP") == 0)
  3163.     {
  3164.         if (pat_ptrs[ITEM_SKIP] != NULL)    /* one skip pattern allowed */
  3165.         {
  3166.         illegal = TRUE;
  3167.         break;
  3168.         }
  3169.         item = ITEM_SKIP;
  3170.     }
  3171.     else
  3172.         break;
  3173.     rest = skipwhite(key_end);
  3174.     if (*rest != '=')
  3175.     {
  3176.         rest = NULL;
  3177.         EMSG2("Missing '=': %s", arg);
  3178.         break;
  3179.     }
  3180.     rest = skipwhite(rest + 1);
  3181.     if (*rest == NUL)
  3182.     {
  3183.         not_enough = TRUE;
  3184.         break;
  3185.     }
  3186.  
  3187.     if (item == ITEM_MATCHGROUP)
  3188.     {
  3189.         p = skiptowhite(rest);
  3190.         if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
  3191.         matchgroup_id = 0;
  3192.         else
  3193.         {
  3194.         matchgroup_id = syn_check_group(rest, (int)(p - rest));
  3195.         if (matchgroup_id == 0)
  3196.         {
  3197.             illegal = TRUE;
  3198.             break;
  3199.         }
  3200.         }
  3201.         rest = skipwhite(p);
  3202.     }
  3203.     else
  3204.     {
  3205.         /*
  3206.          * Allocate room for a syn_pattern, and link it in the list of
  3207.          * syn_patterns for this item, at the start (because the list is
  3208.          * used from end to start).
  3209.          */
  3210.         ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
  3211.         if (ppp == NULL)
  3212.         {
  3213.         rest = NULL;
  3214.         break;
  3215.         }
  3216.         ppp->pp_next = pat_ptrs[item];
  3217.         pat_ptrs[item] = ppp;
  3218.         ppp->pp_synp = (struct syn_pattern *)alloc_clear(
  3219.                     (unsigned)sizeof(struct syn_pattern));
  3220.         if (ppp->pp_synp == NULL)
  3221.         {
  3222.         rest = NULL;
  3223.         break;
  3224.         }
  3225.  
  3226.         /*
  3227.          * Get the syntax pattern and the following offset(s).
  3228.          */
  3229.         rest = get_syn_pattern(rest, ppp->pp_synp);
  3230.         if (item == ITEM_END && vim_regcomp_had_eol())
  3231.         ppp->pp_synp->sp_flags |= HL_HAS_EOL;
  3232.         ppp->pp_matchgroup_id = matchgroup_id;
  3233.         ++pat_count;
  3234.     }
  3235.     }
  3236.     vim_free(key);
  3237.     if (illegal || not_enough)
  3238.     rest = NULL;
  3239.  
  3240.     /*
  3241.      * Must have a "start" and "end" pattern.
  3242.      */
  3243.     if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
  3244.                           pat_ptrs[ITEM_END] == NULL))
  3245.     {
  3246.     not_enough = TRUE;
  3247.     rest = NULL;
  3248.     }
  3249.  
  3250.     if (rest != NULL)
  3251.     {
  3252.     /*
  3253.      * Check for trailing garbage or command.
  3254.      * If OK, add the item.
  3255.      */
  3256.     eap->nextcmd = check_nextcmd(rest);
  3257.     if (!ends_excmd(*rest) || eap->skip)
  3258.         rest = NULL;
  3259.     else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
  3260.         && (syn_id = syn_check_group(arg,
  3261.                        (int)(group_name_end - arg))) != 0)
  3262.     {
  3263.         /*
  3264.          * Store the start/skip/end in the syn_items list
  3265.          */
  3266.         idx = curbuf->b_syn_patterns.ga_len;
  3267.         for (item = ITEM_START; item <= ITEM_END; ++item)
  3268.         {
  3269.         for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
  3270.         {
  3271.             SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
  3272.             SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
  3273.             SYN_ITEMS(curbuf)[idx].sp_type =
  3274.                 (item == ITEM_START) ? SPTYPE_START :
  3275.                 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
  3276.             SYN_ITEMS(curbuf)[idx].sp_flags |= flags;
  3277.             SYN_ITEMS(curbuf)[idx].sp_syn_id = syn_id;
  3278.             SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
  3279.                             ppp->pp_matchgroup_id;
  3280.             if (item == ITEM_START)
  3281.             {
  3282.             SYN_ITEMS(curbuf)[idx].sp_cont_list = cont_list;
  3283.             SYN_ITEMS(curbuf)[idx].sp_next_list = next_list;
  3284.             }
  3285.             ++curbuf->b_syn_patterns.ga_len;
  3286.             --curbuf->b_syn_patterns.ga_room;
  3287.             ++idx;
  3288.         }
  3289.         }
  3290.  
  3291.         redraw_curbuf_later(NOT_VALID);
  3292.         success = TRUE;        /* don't free the progs and patterns now */
  3293.     }
  3294.     }
  3295.  
  3296.     /*
  3297.      * Free the allocated memory.
  3298.      */
  3299.     for (item = ITEM_START; item <= ITEM_END; ++item)
  3300.     for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
  3301.     {
  3302.         if (!success)
  3303.         {
  3304.         vim_free(ppp->pp_synp->sp_prog);
  3305.         vim_free(ppp->pp_synp->sp_pattern);
  3306.         }
  3307.         vim_free(ppp->pp_synp);
  3308.         ppp_next = ppp->pp_next;
  3309.         vim_free(ppp);
  3310.     }
  3311.  
  3312.     if (!success)
  3313.     {
  3314.     vim_free(cont_list);
  3315.     vim_free(next_list);
  3316.     if (not_enough)
  3317.         EMSG2("Not enough arguments: syntax region %s", arg);
  3318.     else if (illegal || rest == NULL)
  3319.         EMSG2(e_invarg2, arg);
  3320.     }
  3321. }
  3322.  
  3323. /*
  3324.  * On first call for current buffer: Init growing array.
  3325.  */
  3326.     static void
  3327. init_syn_patterns()
  3328. {
  3329.     curbuf->b_syn_patterns.ga_itemsize = sizeof(struct syn_pattern);
  3330.     curbuf->b_syn_patterns.ga_growsize = 10;
  3331. }
  3332.  
  3333. /*
  3334.  * Get one pattern for a ":syntax match" or ":syntax region" command.
  3335.  * Stores the pattern and program in a struct syn_pattern.
  3336.  * Returns a pointer to the next argument, or NULL in case of an error.
  3337.  */
  3338.     static char_u *
  3339. get_syn_pattern(arg, ci)
  3340.     char_u        *arg;
  3341.     struct syn_pattern    *ci;
  3342. {
  3343.     char_u    *end;
  3344.     int        *p = NULL;
  3345.     int        idx;
  3346.  
  3347.     /* need at least three chars */
  3348.     if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
  3349.     return NULL;
  3350.  
  3351.     end = skip_regexp(arg + 1, *arg, TRUE);
  3352.     if (*end != *arg)                /* end delimiter not found */
  3353.     {
  3354.     EMSG2("Pattern delimiter not found: %s", arg);
  3355.     return NULL;
  3356.     }
  3357.     /* store the pattern and compiled regexp program */
  3358.     if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
  3359.     return NULL;
  3360.     if ((ci->sp_prog = vim_regcomp(ci->sp_pattern, TRUE)) == NULL)
  3361.     return NULL;
  3362.     ci->sp_ic = curbuf->b_syn_ic;
  3363.  
  3364.     /*
  3365.      * Check for a match, highlight or region offset.
  3366.      */
  3367.     ++end;
  3368.     do
  3369.     {
  3370.     for (idx = SPO_COUNT; --idx >= 0; )
  3371.         if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
  3372.         break;
  3373.     if (idx >= 0)
  3374.     {
  3375.         p = &(ci->sp_offsets[idx]);
  3376.         if (idx != SPO_LC_OFF)
  3377.         switch (end[3])
  3378.         {
  3379.             case 's':   break;
  3380.             case 'b':   break;
  3381.             case 'e':   idx += SPO_COUNT; break;
  3382.             default:    idx = -1; break;
  3383.         }
  3384.         if (idx >= 0)
  3385.         {
  3386.         ci->sp_off_flags |= (1 << idx);
  3387.         if (idx == SPO_LC_OFF)        /* lc=99 */
  3388.         {
  3389.             end += 3;
  3390.             *p = getdigits(&end);
  3391.  
  3392.             /* "lc=" offset automatically sets "ms=" offset */
  3393.             if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
  3394.             {
  3395.             ci->sp_off_flags |= (1 << SPO_MS_OFF);
  3396.             ci->sp_offsets[SPO_MS_OFF] = *p;
  3397.             }
  3398.         }
  3399.         else                /* yy=x+99 */
  3400.         {
  3401.             end += 4;
  3402.             if (*end == '+')
  3403.             {
  3404.             ++end;
  3405.             *p = getdigits(&end);        /* positive offset */
  3406.             }
  3407.             else if (*end == '-')
  3408.             {
  3409.             ++end;
  3410.             *p = -getdigits(&end);        /* negative offset */
  3411.             }
  3412.         }
  3413.         if (*end != ',')
  3414.             break;
  3415.         ++end;
  3416.         }
  3417.     }
  3418.     } while (idx >= 0);
  3419.  
  3420.     if (!ends_excmd(*end) && !vim_iswhite(*end))
  3421.     {
  3422.     EMSG2("Garbage after pattern: %s", arg);
  3423.     return NULL;
  3424.     }
  3425.     return skipwhite(end);
  3426. }
  3427.  
  3428. /*
  3429.  * Handle ":syntax sync .." command.
  3430.  */
  3431. /* ARGSUSED */
  3432.     static void
  3433. syn_cmd_sync(eap, syncing)
  3434.     EXARG    *eap;
  3435.     int        syncing;        /* not used */
  3436. {
  3437.     char_u    *arg_start = eap->arg;
  3438.     char_u    *arg_end;
  3439.     char_u    *key = NULL;
  3440.     char_u    *next_arg;
  3441.     int        illegal = FALSE;
  3442.     int        finished = FALSE;
  3443.  
  3444.     if (ends_excmd(*arg_start))
  3445.     {
  3446.     syn_cmd_list(eap, TRUE);
  3447.     return;
  3448.     }
  3449.  
  3450.     while (!ends_excmd(*arg_start))
  3451.     {
  3452.     arg_end = skiptowhite(arg_start);
  3453.     next_arg = skipwhite(arg_end);
  3454.     vim_free(key);
  3455.     key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
  3456.     if (STRCMP(key, "CCOMMENT") == 0)
  3457.     {
  3458.         curbuf->b_syn_sync_flags |= SF_CCOMMENT;
  3459.         if (!ends_excmd(*next_arg))
  3460.         {
  3461.         arg_end = skiptowhite(next_arg);
  3462.         curbuf->b_syn_sync_id = syn_check_group(next_arg,
  3463.                            (int)(arg_end - next_arg));
  3464.         next_arg = skipwhite(arg_end);
  3465.         }
  3466.         else
  3467.         curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
  3468.     }
  3469.     else if (  STRNCMP(key, "LINES", 5) == 0
  3470.         || STRNCMP(key, "MINLINES", 8) == 0
  3471.         || STRNCMP(key, "MAXLINES", 8) == 0)
  3472.     {
  3473.         if (key[0] == 'L')
  3474.         arg_end = key + 6;
  3475.         else
  3476.         arg_end = key + 9;
  3477.         if (arg_end[-1] != '=' || !isdigit(*arg_end))
  3478.         {
  3479.         illegal = TRUE;
  3480.         break;
  3481.         }
  3482.         if (key[1] == 'A')
  3483.         curbuf->b_syn_sync_maxlines = getdigits(&arg_end);
  3484.         else
  3485.         curbuf->b_syn_sync_minlines = getdigits(&arg_end);
  3486.     }
  3487.     else if (STRCMP(key, "LINECONT") == 0)
  3488.     {
  3489.         if (curbuf->b_syn_linecont_pat != NULL)
  3490.         {
  3491.         EMSG("syntax sync: line continuations pattern specified twice");
  3492.         finished = TRUE;
  3493.         break;
  3494.         }
  3495.         arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE);
  3496.         if (*arg_end != *next_arg)        /* end delimiter not found */
  3497.         {
  3498.         illegal = TRUE;
  3499.         break;
  3500.         }
  3501.  
  3502.         /* store the pattern and compiled regexp program */
  3503.         if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
  3504.                       (int)(arg_end - next_arg - 1))) == NULL)
  3505.         {
  3506.         finished = TRUE;
  3507.         break;
  3508.         }
  3509.         curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
  3510.         if ((curbuf->b_syn_linecont_prog =
  3511.                vim_regcomp(curbuf->b_syn_linecont_pat, TRUE)) == NULL)
  3512.         {
  3513.         vim_free(curbuf->b_syn_linecont_pat);
  3514.         curbuf->b_syn_linecont_pat = NULL;
  3515.         finished = TRUE;
  3516.         break;
  3517.         }
  3518.         next_arg = skipwhite(arg_end + 1);
  3519.     }
  3520.     else
  3521.     {
  3522.         eap->arg = next_arg;
  3523.         if (STRCMP(key, "MATCH") == 0)
  3524.         syn_cmd_match(eap, TRUE);
  3525.         else if (STRCMP(key, "REGION") == 0)
  3526.         syn_cmd_region(eap, TRUE);
  3527.         else if (STRCMP(key, "CLEAR") == 0)
  3528.         syn_cmd_clear(eap, TRUE);
  3529.         else
  3530.         illegal = TRUE;
  3531.         finished = TRUE;
  3532.         break;
  3533.     }
  3534.     arg_start = next_arg;
  3535.     }
  3536.     vim_free(key);
  3537.     if (illegal)
  3538.     EMSG2("Illegal arguments: %s", arg_start);
  3539.     else if (!finished)
  3540.     {
  3541.     eap->nextcmd = check_nextcmd(arg_start);
  3542.     redraw_curbuf_later(NOT_VALID);
  3543.     }
  3544. }
  3545.  
  3546. /*
  3547.  * Convert a line of highlight group names into a list of group ID numbers.
  3548.  * "arg" should point to the "contains" or "nextgroup" keyword.
  3549.  * "arg" is advanced to after the last group name.
  3550.  * Careful: the argument is modified (NULs added).
  3551.  * returns FAIL for some error, OK for success.
  3552.  */
  3553.     static int
  3554. get_id_list(arg, keylen, list)
  3555.     char_u    **arg;
  3556.     int        keylen;        /* length of keyword */
  3557.     short    **list;        /* where to store the resulting list, if not
  3558.                    NULL, the list is silently skipped! */
  3559. {
  3560.     char_u    *p;
  3561.     char_u    *end;
  3562.     int        round;
  3563.     int        count;
  3564.     short    *retval = NULL;
  3565.     char_u    *name;
  3566.     vim_regexp    *prog;
  3567.     int        id;
  3568.     int        i;
  3569.     int        failed = FALSE;
  3570.  
  3571.     /*
  3572.      * We parse the list twice:
  3573.      * round == 1: count the number of items, allocate the array.
  3574.      * round == 2: fill the array with the items.
  3575.      */
  3576.     for (round = 1; round <= 2; ++round)
  3577.     {
  3578.     /*
  3579.      * skip "contains"
  3580.      */
  3581.     p = skipwhite(*arg + keylen);
  3582.     if (*p != '=')
  3583.     {
  3584.         EMSG2("Missing equal sign: %s", *arg);
  3585.         break;
  3586.     }
  3587.     p = skipwhite(p + 1);
  3588.     if (ends_excmd(*p))
  3589.     {
  3590.         EMSG2("Empty argument: %s", *arg);
  3591.         break;
  3592.     }
  3593.  
  3594.     /*
  3595.      * parse the arguments after "contains"
  3596.      */
  3597.     count = 0;
  3598.     while (!ends_excmd(*p))
  3599.     {
  3600.         for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
  3601.         ;
  3602.         name = alloc((int)(end - p + 3));        /* leave room for "^$" */
  3603.         if (name == NULL)
  3604.         {
  3605.         failed = TRUE;
  3606.         break;
  3607.         }
  3608.         STRNCPY(name + 1, p, end - p);
  3609.         name[end - p + 1] = NUL;
  3610.         if (       STRCMP(name + 1, "ALLBUT") == 0
  3611.             || STRCMP(name + 1, "ALL") == 0)
  3612.         {
  3613.         if (TO_UPPER(**arg) != 'C')
  3614.         {
  3615.             EMSG2("%s not allowed here", name + 1);
  3616.             failed = TRUE;
  3617.             vim_free(name);
  3618.             break;
  3619.         }
  3620.         if (count != 0)
  3621.         {
  3622.             EMSG2("%s must be first in contains list", name + 1);
  3623.             failed = TRUE;
  3624.             vim_free(name);
  3625.             break;
  3626.         }
  3627.         id = CONTAINS_ALLBUT;
  3628.         }
  3629.         else
  3630.         {
  3631.         /*
  3632.          * Handle full group name.
  3633.          */
  3634.         if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
  3635.             id = syn_check_group(name + 1, (int)(end - p));
  3636.         else
  3637.         {
  3638.             /*
  3639.              * Handle match of regexp with group names.
  3640.              */
  3641.             *name = '^';
  3642.             STRCAT(name, "$");
  3643.             prog = vim_regcomp(name, TRUE);
  3644.             if (prog == NULL)
  3645.             {
  3646.             failed = TRUE;
  3647.             vim_free(name);
  3648.             break;
  3649.             }
  3650.  
  3651.             reg_ic = TRUE;
  3652.             id = 0;
  3653.             for (i = highlight_ga.ga_len; --i >= 0; )
  3654.             {
  3655.             if (vim_regexec(prog, HL_TABLE()[i].sg_name, TRUE))
  3656.             {
  3657.                 if (round == 2)
  3658.                 retval[count] = i + 1;
  3659.                 ++count;
  3660.                 id = -1;        /* remember that we found one */
  3661.             }
  3662.             }
  3663.             vim_free(prog);
  3664.         }
  3665.         }
  3666.         vim_free(name);
  3667.         if (id == 0)
  3668.         {
  3669.         EMSG2("Unknown group name: %s", p);
  3670.         failed = TRUE;
  3671.         break;
  3672.         }
  3673.         if (id > 0)
  3674.         {
  3675.         if (round == 2)
  3676.             retval[count] = id;
  3677.         ++count;
  3678.         }
  3679.         p = skipwhite(end);
  3680.         if (*p != ',')
  3681.         break;
  3682.         p = skipwhite(p + 1);    /* skip comma in between arguments */
  3683.     }
  3684.     if (failed)
  3685.         break;
  3686.     if (round == 1)
  3687.     {
  3688.         retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
  3689.         if (retval == NULL)
  3690.         break;
  3691.         retval[count] = 0;        /* zero means end of the list */
  3692.     }
  3693.     }
  3694.  
  3695.     *arg = p;
  3696.     if (failed || retval == NULL)
  3697.     {
  3698.     vim_free(retval);
  3699.     return FAIL;
  3700.     }
  3701.  
  3702.     if (*list == NULL)
  3703.     *list = retval;
  3704.     else
  3705.     vim_free(retval);    /* list already found, don't overwrite it */
  3706.  
  3707.     return OK;
  3708. }
  3709.  
  3710. /*
  3711.  * Make a copy of an ID list.
  3712.  */
  3713.     static short *
  3714. copy_id_list(list)
  3715.     short   *list;
  3716. {
  3717.     int        len;
  3718.     int        count;
  3719.     short   *retval;
  3720.  
  3721.     if (list == NULL)
  3722.     return NULL;
  3723.  
  3724.     for (count = 0; list[count]; ++count)
  3725.     ;
  3726.     len = (count + 1) * sizeof(short);
  3727.     retval = (short *)alloc((unsigned)len);
  3728.     if (retval != NULL)
  3729.     vim_memmove(retval, list, (size_t)len);
  3730.  
  3731.     return retval;
  3732. }
  3733.  
  3734. /*
  3735.  * Check if "id" is in the "contains" or "nextgroup" list of pattern "idx".
  3736.  */
  3737.     static int
  3738. in_id_list(list, id, contained)
  3739.     short   *list;        /* id list */
  3740.     int        id;            /* group id */
  3741.     int        contained;        /* group id is contained */
  3742. {
  3743.     /*
  3744.      * If list is ID_LIST_ALL, we are in a transparent item that isn't
  3745.      * inside anything.  Only allow not-contained groups.
  3746.      */
  3747.     if (list == ID_LIST_ALL)
  3748.     return !contained;
  3749.  
  3750.     /*
  3751.      * If the first item is "ALLBUT", return TRUE if id is NOT in the contains
  3752.      * list.
  3753.      */
  3754.     if (*list == CONTAINS_ALLBUT)
  3755.     {
  3756.     ++list;
  3757.     while (*list)
  3758.         if (*list++ == id)
  3759.         return FALSE;
  3760.     return TRUE;
  3761.     }
  3762.  
  3763.     /*
  3764.      * Return TRUE if id is in the contains list.
  3765.      */
  3766.     while (*list)
  3767.     if (*list++ == id)
  3768.         return TRUE;
  3769.     return FALSE;
  3770. }
  3771.  
  3772. struct subcommand
  3773. {
  3774.     char    *name;                /* subcommand name */
  3775.     void    (*func)__ARGS((EXARG *, int));    /* function to call */
  3776. };
  3777.  
  3778. static struct subcommand subcommands[] =
  3779. {
  3780.     {"case",        syn_cmd_case},
  3781.     {"clear",        syn_cmd_clear},
  3782.     {"keyword",        syn_cmd_keyword},
  3783.     {"list",        syn_cmd_list},
  3784.     {"match",        syn_cmd_match},
  3785.     {"on",        syn_cmd_on},
  3786.     {"off",        syn_cmd_off},
  3787.     {"region",        syn_cmd_region},
  3788.     {"sync",        syn_cmd_sync},
  3789.     {"",        syn_cmd_list},
  3790.     {NULL, NULL}
  3791. };
  3792.  
  3793. /*
  3794.  * Handle the ":syntax" command.
  3795.  * This searches the subcommands[] table for the subcommand name, and calls a
  3796.  * syntax_subcommand() function to do the rest.
  3797.  */
  3798.     void
  3799. do_syntax(eap)
  3800.     EXARG    *eap;
  3801. {
  3802.     char_u  *arg = eap->arg;
  3803.     char_u  *subcmd_end;
  3804.     char_u  *subcmd_name;
  3805.     int        i;
  3806.  
  3807.     /* isolate subcommand name */
  3808.     for (subcmd_end = arg; isalpha(*subcmd_end); ++subcmd_end)
  3809.     ;
  3810.     subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
  3811.     if (subcmd_name != NULL)
  3812.     {
  3813.     if (eap->skip)        /* skip error messages for all subcommands */
  3814.         ++emsg_off;
  3815.     for (i = 0; ; ++i)
  3816.     {
  3817.         if (subcommands[i].name == NULL)
  3818.         {
  3819.         EMSG2("Invalid :syntax subcommand: %s", subcmd_name);
  3820.         break;
  3821.         }
  3822.         if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
  3823.         {
  3824.         eap->arg = skipwhite(subcmd_end);
  3825.         (subcommands[i].func)(eap, FALSE);
  3826.         break;
  3827.         }
  3828.     }
  3829.     vim_free(subcmd_name);
  3830.     if (eap->skip)
  3831.         --emsg_off;
  3832.     }
  3833. }
  3834.  
  3835.     int
  3836. syntax_present(buf)
  3837.     BUF        *buf;
  3838. {
  3839.     return (buf->b_syn_patterns.ga_len != 0
  3840.         || curbuf->b_keywtab != NULL
  3841.         || curbuf->b_keywtab_ic != NULL);
  3842. }
  3843.  
  3844. static enum
  3845. {
  3846.     EXP_SUBCMD,        /* expand ":syn" sub-commands */
  3847.     EXP_CASE        /* expand ":syn case" arguments */
  3848. } expand_what;
  3849.  
  3850.  
  3851. /*
  3852.  * Handle command line completion for :syntax command.
  3853.  */
  3854.     void
  3855. set_context_in_syntax_cmd(arg)
  3856.     char_u *arg;
  3857. {
  3858.     char_u    *p;
  3859.  
  3860.     /* Default: expand subcommands */
  3861.     expand_context = EXPAND_SYNTAX;
  3862.     expand_what = EXP_SUBCMD;
  3863.     expand_pattern = arg;
  3864.     include_link = FALSE;
  3865.  
  3866.     /* (part of) subcommand already typed */
  3867.     if (*arg != NUL)
  3868.     {
  3869.     p = skiptowhite(arg);
  3870.     if (*p != NUL)            /* past first word */
  3871.     {
  3872.         expand_pattern = skipwhite(p);
  3873.         if (*skiptowhite(expand_pattern) != NUL)
  3874.         expand_context = EXPAND_NOTHING;
  3875.         else if (STRNICMP(arg, "case", p - arg) == 0)
  3876.         expand_what = EXP_CASE;
  3877.         else if (  STRNICMP(arg, "keyword", p - arg) == 0
  3878.             || STRNICMP(arg, "region", p - arg) == 0
  3879.             || STRNICMP(arg, "match", p - arg) == 0
  3880.             || STRNICMP(arg, "list", p - arg) == 0)
  3881.         expand_context = EXPAND_HIGHLIGHT;
  3882.         else
  3883.         expand_context = EXPAND_NOTHING;
  3884.     }
  3885.     }
  3886. }
  3887.  
  3888. static char *(case_args[]) = {"match", "ignore", NULL};
  3889.  
  3890. /*
  3891.  * Function given to ExpandGeneric() to obtain the list syntax names for
  3892.  * expansion.
  3893.  */
  3894.     char_u *
  3895. get_syntax_name(idx)
  3896.     int        idx;
  3897. {
  3898.     if (expand_what == EXP_SUBCMD)
  3899.     return (char_u *)subcommands[idx].name;
  3900.     return (char_u *)case_args[idx];
  3901. }
  3902.  
  3903. #ifdef WANT_EVAL
  3904. /*
  3905.  * Function called for expression evaluation: get syntax ID at file position.
  3906.  */
  3907.     int
  3908. syn_get_id(line, col, trans)
  3909.     long    line;
  3910.     long    col;
  3911.     int        trans;        /* remove transparancy */
  3912. {
  3913.     if (curwin->w_buffer != syn_buf
  3914.                || col < (long)current_col || line != current_lnum)
  3915.     syntax_start(curwin, line);
  3916.  
  3917.     (void)get_syntax_attr((colnr_t)col, ml_get((linenr_t)line));
  3918.  
  3919.     return (trans ? current_trans_id : current_id);
  3920. }
  3921. #endif
  3922.  
  3923. #endif /* SYNTAX_HL */
  3924.  
  3925.  
  3926. /**************************************
  3927.  *  Highlighting stuff              *
  3928.  **************************************/
  3929.  
  3930. /*
  3931.  * The default highlight groups.  Used in the 'highlight' and 'guicursor'
  3932.  * options default.  Depends on 'background' option.
  3933.  */
  3934. static char *(highlight_init_both[]) =
  3935.     {
  3936. #ifdef USE_GUI
  3937.     "Cursor guibg=fg guifg=bg",
  3938. #endif
  3939.     "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
  3940.     "link IncSearch Visual",
  3941.     "ModeMsg term=bold cterm=bold gui=bold",
  3942.     "NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
  3943.     "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
  3944.     "StatusLineNC term=reverse cterm=reverse gui=reverse",
  3945.     "Visual term=reverse cterm=reverse gui=reverse",
  3946.     NULL
  3947.     };
  3948.  
  3949. static char *(highlight_init_light[]) =
  3950.     {
  3951.     "Directory term=bold ctermfg=DarkBlue guifg=Blue",
  3952.     "LineNr term=underline ctermfg=Brown guifg=Brown",
  3953.     "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
  3954.     "Normal gui=NONE",
  3955.     "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
  3956.     "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
  3957.     "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
  3958.     "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
  3959.     "WarningMsg term=standout ctermfg=DarkRed guifg=Red",
  3960.     NULL
  3961.     };
  3962.  
  3963. static char *(highlight_init_dark[]) =
  3964.     {
  3965.     "Directory term=bold ctermfg=LightCyan guifg=Blue",
  3966.     "LineNr term=underline ctermfg=Yellow guifg=Yellow",
  3967.     "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
  3968.     "Normal gui=NONE",
  3969.     "Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
  3970.     "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
  3971.     "SpecialKey term=bold ctermfg=LightBlue guifg=Blue",
  3972.     "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
  3973.     "WarningMsg term=standout ctermfg=LightRed guifg=Red",
  3974.     NULL
  3975.     };
  3976.  
  3977.     void
  3978. init_highlight(both)
  3979.     int        both;        /* include groups where 'bg' doesn't matter */
  3980. {
  3981.     int        i;
  3982.     char    **pp;
  3983.  
  3984.     if (both)
  3985.     {
  3986.     pp = highlight_init_both;
  3987.     for (i = 0; pp[i] != NULL; ++i)
  3988.         do_highlight((char_u *)pp[i], FALSE, TRUE);
  3989.     }
  3990.  
  3991.     if (TO_LOWER(*p_bg) == 'l')
  3992.     pp = highlight_init_light;
  3993.     else
  3994.     pp = highlight_init_dark;
  3995.     for (i = 0; pp[i] != NULL; ++i)
  3996.     do_highlight((char_u *)pp[i], FALSE, TRUE);
  3997. }
  3998.  
  3999. /*
  4000.  * Handle the ":highlight .." command.
  4001.  */
  4002.     void
  4003. do_highlight(line, forceit, init)
  4004.     char_u    *line;
  4005.     int        forceit;
  4006.     int        init;        /* TRUE when called for initializing */
  4007. {
  4008.     char_u    *name_end;
  4009.     char_u    *p;
  4010.     char_u    *linep;
  4011.     char_u    *key_start;
  4012.     char_u    *arg_start;
  4013.     char_u    *key = NULL, *arg = NULL;
  4014.     int        i;
  4015.     int        off;
  4016.     int        len;
  4017.     int        attr;
  4018.     int        id;
  4019.     int        idx;
  4020.     int        doclear = FALSE;
  4021.     int        dolink = FALSE;
  4022.     int        error = FALSE;
  4023.     int        color;
  4024.     int        is_normal_group = FALSE;    /* "Normal" group */
  4025. #ifdef USE_GUI_X11
  4026.     int        is_menu_group = FALSE;        /* "Menu" group */
  4027.     int        is_scrollbar_group = FALSE;    /* "Scrollbar" group */
  4028. #endif
  4029.  
  4030.     /*
  4031.      * If no argument, list current highlighting.
  4032.      */
  4033.     if (ends_excmd(*line))
  4034.     {
  4035.     for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
  4036.         /* TODO: only call when the group has attributes set */
  4037.         highlight_list_one(i);
  4038.     return;
  4039.     }
  4040.  
  4041.     /*
  4042.      * Isolate the name.
  4043.      */
  4044.     name_end = skiptowhite(line);
  4045.     linep = skipwhite(name_end);
  4046.  
  4047.     if (STRNCMP(line, "clear", name_end - line) == 0)
  4048.     doclear = TRUE;
  4049.     if (STRNCMP(line, "link", name_end - line) == 0)
  4050.     dolink = TRUE;
  4051.  
  4052.     /*
  4053.      * ":highlight {group-name}": list highlighting for one group.
  4054.      */
  4055.     if (!doclear && !dolink && ends_excmd(*linep))
  4056.     {
  4057.     id = syn_namen2id(line, (int)(name_end - line));
  4058.     if (id == 0)
  4059.         EMSG2("highlight group not found: %s", line);
  4060.     else
  4061.         highlight_list_one(id);
  4062.     return;
  4063.     }
  4064.  
  4065.     /*
  4066.      * Handle ":highlight link {from} {to}" command.
  4067.      */
  4068.     if (dolink)
  4069.     {
  4070.     char_u        *from_start = linep;
  4071.     char_u        *from_end;
  4072.     char_u        *to_start;
  4073.     char_u        *to_end;
  4074.     int        from_id;
  4075.     int        to_id;
  4076.  
  4077.     from_end = skiptowhite(from_start);
  4078.     to_start = skipwhite(from_end);
  4079.     to_end     = skiptowhite(to_start);
  4080.  
  4081.     if (ends_excmd(*from_start) || ends_excmd(*to_start))
  4082.     {
  4083.         EMSG2("Not enough arguments: \":highlight link %s\"", from_start);
  4084.         return;
  4085.     }
  4086.  
  4087.     if (!ends_excmd(*skipwhite(to_end)))
  4088.     {
  4089.         EMSG2("Too many arguments: \":highlight link %s\"", from_start);
  4090.         return;
  4091.     }
  4092.  
  4093.     from_id = syn_check_group(from_start, (int)(from_end - from_start));
  4094.     if (STRNCMP(to_start, "NONE", 4) == 0)
  4095.         to_id = 0;
  4096.     else
  4097.         to_id = syn_check_group(to_start, (int)(to_end - to_start));
  4098.  
  4099.     if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
  4100.     {
  4101.         /*
  4102.          * Don't allow a link when there already is some highlighting
  4103.          * for the group, unless '!' is used
  4104.          */
  4105.         if (to_id > 0 && !forceit && !init
  4106.             &&      (HL_TABLE()[from_id - 1].sg_term_attr != 0
  4107.             || HL_TABLE()[from_id - 1].sg_cterm_attr != 0
  4108. #ifdef USE_GUI
  4109.             || HL_TABLE()[from_id - 1].sg_gui_attr != 0
  4110. #endif
  4111.                ))
  4112.         {
  4113.         if (sourcing_name == NULL)
  4114.             EMSG("group has settings, highlight link ignored");
  4115.         }
  4116.         else
  4117.         {
  4118.         if (!init)
  4119.             HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
  4120.         HL_TABLE()[from_id - 1].sg_link = to_id;
  4121.         }
  4122.     }
  4123.  
  4124.     redraw_curbuf_later(NOT_VALID);
  4125.     return;
  4126.     }
  4127.  
  4128.     /*
  4129.      * Handle ":highlight clear {group}" command.
  4130.      */
  4131.     if (doclear)
  4132.     {
  4133.     line = linep;
  4134.     if (ends_excmd(*line))
  4135.     {
  4136.         EMSG("Cannot clear all highlight groups");
  4137.         return;
  4138.     }
  4139.     name_end = skiptowhite(line);
  4140.     linep = skipwhite(name_end);
  4141.     }
  4142.  
  4143.     /*
  4144.      * Find the group name in the table.  If it does not exist yet, add it.
  4145.      */
  4146.     id = syn_check_group(line, (int)(name_end - line));
  4147.     if (id == 0)            /* failed (out of memory) */
  4148.     return;
  4149.     idx = id - 1;            /* index is ID minus one */
  4150.     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
  4151.     is_normal_group = TRUE;
  4152. #ifdef USE_GUI_X11
  4153.     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
  4154.     is_menu_group = TRUE;
  4155.     else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
  4156.     is_scrollbar_group = TRUE;
  4157. #endif
  4158.  
  4159.     if (doclear)
  4160.     highlight_clear(idx);
  4161.     else
  4162.       while (!ends_excmd(*linep))
  4163.       {
  4164.     key_start = linep;
  4165.     if (*linep == '=')
  4166.     {
  4167.         EMSG2("unexpected equal sign: %s", key_start);
  4168.         error = TRUE;
  4169.         break;
  4170.     }
  4171.  
  4172.     /*
  4173.      * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
  4174.      * "guibg").
  4175.      */
  4176.     while (*linep && !vim_iswhite(*linep) && *linep != '=')
  4177.         ++linep;
  4178.     vim_free(key);
  4179.     key = vim_strnsave_up(key_start, (int)(linep - key_start));
  4180.     if (key == NULL)
  4181.     {
  4182.         error = TRUE;
  4183.         break;
  4184.     }
  4185.     linep = skipwhite(linep);
  4186.  
  4187.     if (STRCMP(key, "NONE") == 0)
  4188.     {
  4189.         if (!init || HL_TABLE()[idx].sg_set == 0)
  4190.         {
  4191.         if (!init)
  4192.             HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
  4193.         highlight_clear(idx);
  4194.         }
  4195.         continue;
  4196.     }
  4197.  
  4198.     /*
  4199.      * Check for the equal sign.
  4200.      */
  4201.     if (*linep != '=')
  4202.     {
  4203.         EMSG2("missing equal sign: %s", key_start);
  4204.         error = TRUE;
  4205.         break;
  4206.     }
  4207.     ++linep;
  4208.  
  4209.     /*
  4210.      * Isolate the argument.
  4211.      */
  4212.     linep = skipwhite(linep);
  4213.     if (*linep == '\'')        /* guifg='color name' */
  4214.     {
  4215.         arg_start = ++linep;
  4216.         linep = vim_strchr(linep, '\'');
  4217.     }
  4218.     else
  4219.     {
  4220.         arg_start = linep;
  4221.         linep = skiptowhite(linep);
  4222.     }
  4223.     if (linep == arg_start)
  4224.     {
  4225.         EMSG2("missing argument: %s", key_start);
  4226.         error = TRUE;
  4227.         break;
  4228.     }
  4229.     vim_free(arg);
  4230.     arg = vim_strnsave(arg_start, (int)(linep - arg_start));
  4231.     if (arg == NULL)
  4232.     {
  4233.         error = TRUE;
  4234.         break;
  4235.     }
  4236.     if (*linep == '\'')
  4237.         ++linep;
  4238.  
  4239.     /*
  4240.      * Store the argument.
  4241.      */
  4242.     if (  STRCMP(key, "TERM") == 0
  4243.         || STRCMP(key, "CTERM") == 0
  4244.         || STRCMP(key, "GUI") == 0)
  4245.     {
  4246.         attr = 0;
  4247.         off = 0;
  4248.         while (arg[off] != NUL)
  4249.         {
  4250.         for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
  4251.         {
  4252.             len = STRLEN(hl_name_table[i]);
  4253.             if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
  4254.             {
  4255.             attr |= hl_attr_table[i];
  4256.             off += len;
  4257.             break;
  4258.             }
  4259.         }
  4260.         if (i < 0)
  4261.         {
  4262.             EMSG2("Illegal value: %s", arg);
  4263.             error = TRUE;
  4264.             break;
  4265.         }
  4266.         if (arg[off] == ',')        /* another one follows */
  4267.             ++off;
  4268.         }
  4269.         if (error)
  4270.         break;
  4271.         if (*key == 'T')
  4272.         {
  4273.         if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
  4274.         {
  4275.             if (!init)
  4276.             HL_TABLE()[idx].sg_set |= SG_TERM;
  4277.             HL_TABLE()[idx].sg_term = attr;
  4278.         }
  4279.         }
  4280.         else if (*key == 'C')
  4281.         {
  4282.         if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
  4283.         {
  4284.             if (!init)
  4285.             HL_TABLE()[idx].sg_set |= SG_CTERM;
  4286.             HL_TABLE()[idx].sg_cterm = attr;
  4287.             HL_TABLE()[idx].sg_cterm_bold = FALSE;
  4288.         }
  4289.         }
  4290. #ifdef USE_GUI
  4291.         else
  4292.         {
  4293.         if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
  4294.         {
  4295.             if (!init)
  4296.             HL_TABLE()[idx].sg_set |= SG_GUI;
  4297.             HL_TABLE()[idx].sg_gui = attr;
  4298.         }
  4299.         }
  4300. #endif
  4301.     }
  4302.     else if (STRCMP(key, "FONT") == 0)
  4303.     {
  4304. #ifdef USE_GUI        /* in non-GUI fonts are simply ignored */
  4305.         HL_TABLE()[idx].sg_font = font_name2handle(arg);
  4306.         vim_free(HL_TABLE()[idx].sg_font_name);
  4307.         HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
  4308.         if (is_normal_group)
  4309.         gui_init_font(arg);
  4310. #endif
  4311.     }
  4312.     else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
  4313.     {
  4314.       if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
  4315.       {
  4316.         if (!init)
  4317.         HL_TABLE()[idx].sg_set |= SG_CTERM;
  4318.  
  4319.         /* When setting the foreground color, and previously the "bold"
  4320.          * flag was set for a light color, reset it now */
  4321.         if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
  4322.         {
  4323.         HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
  4324.         HL_TABLE()[idx].sg_cterm_bold = FALSE;
  4325.         }
  4326.  
  4327.         if (isdigit(*arg))
  4328.         color = atoi((char *)arg);
  4329.         else
  4330.         {
  4331.         static char *(color_names[26]) = {
  4332.                 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
  4333.                 "DarkRed", "DarkMagenta", "Brown", "Gray", "Grey",
  4334.                 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
  4335.                 "Blue", "LightBlue", "Green", "LightGreen",
  4336.                 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
  4337.                 "LightMagenta", "Yellow", "White", "NONE"};
  4338.         static int color_numbers_16[26] = {0, 1, 2, 3,
  4339.                          4, 5, 6, 7, 7,
  4340.                          7, 7, 8, 8,
  4341.                          9, 9, 10, 10,
  4342.                          11, 11, 12, 12, 13,
  4343.                          13, 14, 15, -1};
  4344.         /* for terminals with less than 16 colors... */
  4345.         static int color_numbers_8[26] = {0, 4, 2, 6,
  4346.                          1, 5, 3, 7, 7,
  4347.                          7, 7, 0+8, 0+8,
  4348.                          4+8, 4+8, 2+8, 2+8,
  4349.                          6+8, 6+8, 1+8, 1+8, 5+8,
  4350.                          5+8, 3+8, 7+8, -1};
  4351.  
  4352.         /* reduce calls to STRICMP a bit, it can be slow */
  4353.         off = TO_UPPER(*arg);
  4354.         for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
  4355.             if (off == color_names[i][0]
  4356.                  && STRICMP(arg + 1, color_names[i] + 1) == 0)
  4357.             break;
  4358.         if (i < 0)
  4359.         {
  4360.             EMSG2("Color name or number not recognized: %s", key_start);
  4361.             error = TRUE;
  4362.             break;
  4363.         }
  4364.         color = color_numbers_16[i];
  4365.         if (color >= 0)
  4366.         {
  4367.             if (atoi((char *)T_CCO) == 8)
  4368.             {
  4369.             color = color_numbers_8[i];
  4370.             if (key[5] == 'F')
  4371.             {
  4372.                 /* set/reset bold attribute to get light foreground
  4373.                  * colors (on some terminals, e.g. "linux") */
  4374.                 if (color & 8)
  4375.                 {
  4376.                 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
  4377.                 HL_TABLE()[idx].sg_cterm_bold = TRUE;
  4378.                 }
  4379.                 else
  4380.                 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
  4381.             }
  4382.             color &= 7;
  4383.             }
  4384.             else if (atoi((char *)T_CCO) == 16)
  4385.             {
  4386.             /*
  4387.              * Guess: if the termcap entry ends in 'm', it is
  4388.              * probably an xterm-like terminal.  Use the changed
  4389.              * order for colors.
  4390.              */
  4391.             if (*T_CAF != NUL)
  4392.                 p = T_CAF;
  4393.             else
  4394.                 p = T_CSF;
  4395.             if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
  4396.                 color = color_numbers_8[i];
  4397.             }
  4398.         }
  4399.         }
  4400.         /* Add one to the argument, to avoid zero */
  4401.         if (key[5] == 'F')
  4402.         {
  4403.         HL_TABLE()[idx].sg_cterm_fg = color + 1;
  4404.         if (is_normal_group)
  4405.         {
  4406.             cterm_normal_fg_color = color + 1;
  4407.             cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
  4408.             must_redraw = CLEAR;
  4409.             term_fg_color(color);
  4410.         }
  4411.         }
  4412.         else
  4413.         {
  4414.         HL_TABLE()[idx].sg_cterm_bg = color + 1;
  4415.         if (is_normal_group)
  4416.         {
  4417.             cterm_normal_bg_color = color + 1;
  4418.             must_redraw = CLEAR;
  4419.             term_bg_color(color);
  4420.             if (atoi((char *)T_CCO) < 16)
  4421.             i = (color == 0 || color == 4);
  4422.             else
  4423.             i = (color < 7 || color == 8);
  4424.             set_option_value((char_u *)"bg", 0L,
  4425.                     i ? (char_u *)"dark" : (char_u *)"light");
  4426.         }
  4427.         }
  4428.       }
  4429.     }
  4430.     else if (STRCMP(key, "GUIFG") == 0)
  4431.     {
  4432. #ifdef USE_GUI        /* in non-GUI guifg colors are simply ignored */
  4433.       if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
  4434.       {
  4435.         if (!init)
  4436.         HL_TABLE()[idx].sg_set |= SG_GUI;
  4437.  
  4438.         /* Add one to the argument, to avoid zero */
  4439.         i = color_name2handle(arg) + 1;
  4440.         if (i > 0 || STRCMP(arg, "NONE") == 0 || !gui.in_use)
  4441.         {
  4442.         HL_TABLE()[idx].sg_gui_fg = i;
  4443.         vim_free(HL_TABLE()[idx].sg_gui_fg_name);
  4444.         if (STRCMP(arg, "NONE"))
  4445.             HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
  4446.         else
  4447.             HL_TABLE()[idx].sg_gui_fg_name = NULL;
  4448. # ifdef USE_GUI_X11
  4449.         if (is_menu_group)
  4450.             gui.menu_fg_pixel = i - 1;
  4451.         if (is_scrollbar_group)
  4452.             gui.scroll_fg_pixel = i - 1;
  4453. # endif
  4454.         }
  4455.       }
  4456. #endif
  4457.     }
  4458.     else if (STRCMP(key, "GUIBG") == 0)
  4459.     {
  4460. #ifdef USE_GUI        /* in non-GUI guibg colors are simply ignored */
  4461.       if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
  4462.       {
  4463.         if (!init)
  4464.         HL_TABLE()[idx].sg_set |= SG_GUI;
  4465.  
  4466.         /* Add one to the argument, to avoid zero */
  4467.         i = color_name2handle(arg) + 1;
  4468.         if (i > 0 || STRCMP(arg, "NONE") == 0 || !gui.in_use)
  4469.         {
  4470.         HL_TABLE()[idx].sg_gui_bg = i;
  4471.         vim_free(HL_TABLE()[idx].sg_gui_bg_name);
  4472.         if (STRCMP(arg, "NONE"))
  4473.             HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
  4474.         else
  4475.             HL_TABLE()[idx].sg_gui_bg_name = NULL;
  4476. # ifdef USE_GUI_X11
  4477.         if (is_menu_group)
  4478.             gui.menu_bg_pixel = i - 1;
  4479.         if (is_scrollbar_group)
  4480.             gui.scroll_bg_pixel = i - 1;
  4481. # endif
  4482.         }
  4483.       }
  4484. #endif
  4485.     }
  4486.     else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
  4487.     {
  4488.         char_u    buf[100];
  4489.         char_u    *tname;
  4490.  
  4491.         if (!init)
  4492.         HL_TABLE()[idx].sg_set |= SG_TERM;
  4493.  
  4494.         /*
  4495.          * The "start" and "stop"  arguments can be a literal escape
  4496.          * sequence, or a comma seperated list of terminal codes.
  4497.          */
  4498.         if (STRNCMP(arg, "t_", 2) == 0)
  4499.         {
  4500.         off = 0;
  4501.         buf[0] = 0;
  4502.         while (arg[off] != NUL)
  4503.         {
  4504.             /* Isolate one termcap name */
  4505.             for (len = 0; arg[off + len] &&
  4506.                          arg[off + len] != ','; ++len)
  4507.             ;
  4508.             tname = vim_strnsave(arg + off, len);
  4509.             if (tname == NULL)        /* out of memory */
  4510.             {
  4511.             error = TRUE;
  4512.             break;
  4513.             }
  4514.             /* lookup the escape sequence for the item */
  4515.             p = get_term_code(tname);
  4516.             vim_free(tname);
  4517.             if (p == NULL)        /* ignore non-existing things */
  4518.             p = (char_u *)"";
  4519.  
  4520.             /* Append it to the already found stuff */
  4521.             if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
  4522.             {
  4523.             EMSG2("terminal code too long: %s", arg);
  4524.             error = TRUE;
  4525.             break;
  4526.             }
  4527.             STRCAT(buf, p);
  4528.  
  4529.             /* Advance to the next item */
  4530.             off += len;
  4531.             if (arg[off] == ',')        /* another one follows */
  4532.             ++off;
  4533.         }
  4534.         }
  4535.         else
  4536.         {
  4537.         /*
  4538.          * Copy characters from arg[] to buf[], translating <> codes.
  4539.          */
  4540.         for (p = arg, off = 0; off < 100 && *p; )
  4541.         {
  4542.             len = trans_special(&p, buf + off);
  4543.             if (len)            /* recognized special char */
  4544.             off += len;
  4545.             else            /* copy as normal char */
  4546.             buf[off++] = *p++;
  4547.         }
  4548.         buf[off] = NUL;
  4549.         }
  4550.         if (error)
  4551.         break;
  4552.  
  4553.         if (STRCMP(buf, "NONE") == 0)    /* resetting the value */
  4554.         p = NULL;
  4555.         else
  4556.         p = vim_strsave(buf);
  4557.         if (key[2] == 'A')
  4558.         {
  4559.         vim_free(HL_TABLE()[idx].sg_start);
  4560.         HL_TABLE()[idx].sg_start = p;
  4561.         }
  4562.         else
  4563.         {
  4564.         vim_free(HL_TABLE()[idx].sg_stop);
  4565.         HL_TABLE()[idx].sg_stop = p;
  4566.         }
  4567.     }
  4568.     else
  4569.     {
  4570.         EMSG2("Illegal argument: %s", key_start);
  4571.         error = TRUE;
  4572.         break;
  4573.     }
  4574.  
  4575.     /*
  4576.      * When highlighting has been given for a group, don't link it.
  4577.      */
  4578.     if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
  4579.         HL_TABLE()[idx].sg_link = 0;
  4580.  
  4581.     /*
  4582.      * Continue with next argument.
  4583.      */
  4584.     linep = skipwhite(linep);
  4585.       }
  4586.  
  4587.     /*
  4588.      * If there is an error, and it's a new entry, remove it from the table.
  4589.      */
  4590.     if (error && idx == highlight_ga.ga_len)
  4591.     syn_unadd_group();
  4592.     else
  4593.     {
  4594.     if (is_normal_group)
  4595.     {
  4596.         HL_TABLE()[idx].sg_term_attr = 0;
  4597.         HL_TABLE()[idx].sg_cterm_attr = 0;
  4598. #ifdef USE_GUI
  4599.         HL_TABLE()[idx].sg_gui_attr = 0;
  4600.         /*
  4601.          * Need to update all groups, because they might be using "bg"
  4602.          * and/or "fg", which have been changed now.
  4603.          */
  4604.         if (gui.in_use)
  4605.         highlight_gui_started();
  4606. #endif
  4607.     }
  4608. #ifdef USE_GUI_X11
  4609.     else if (is_menu_group)
  4610.         gui_mch_new_menu_colors();
  4611.     else if (is_scrollbar_group)
  4612.     {
  4613.         if (gui.in_use)
  4614.         gui_new_scrollbar_colors();
  4615.     }
  4616. #endif
  4617.     else
  4618.         set_hl_attr(idx);
  4619.     redraw_all_later(NOT_VALID);
  4620.     }
  4621.     vim_free(key);
  4622.     vim_free(arg);
  4623.  
  4624.     /* Only call highlight_changed() once, after sourcing a syntax file */
  4625.     need_highlight_changed = TRUE;
  4626. }
  4627.  
  4628. /*
  4629.  * Clear highlighting for one group.
  4630.  */
  4631.     static void
  4632. highlight_clear(idx)
  4633.     int idx;
  4634. {
  4635.     HL_TABLE()[idx].sg_term = 0;
  4636.     vim_free(HL_TABLE()[idx].sg_start);
  4637.     HL_TABLE()[idx].sg_start = NULL;
  4638.     vim_free(HL_TABLE()[idx].sg_stop);
  4639.     HL_TABLE()[idx].sg_stop = NULL;
  4640.     HL_TABLE()[idx].sg_term_attr = 0;
  4641.     HL_TABLE()[idx].sg_cterm = 0;
  4642.     HL_TABLE()[idx].sg_cterm_bold = FALSE;
  4643.     HL_TABLE()[idx].sg_cterm_fg = 0;
  4644.     HL_TABLE()[idx].sg_cterm_bg = 0;
  4645.     HL_TABLE()[idx].sg_cterm_attr = 0;
  4646. #ifdef USE_GUI        /* in non-GUI fonts are simply ignored */
  4647.     HL_TABLE()[idx].sg_gui = 0;
  4648.     HL_TABLE()[idx].sg_gui_fg = 0;
  4649.     vim_free(HL_TABLE()[idx].sg_gui_fg_name);
  4650.     HL_TABLE()[idx].sg_gui_fg_name = NULL;
  4651.     HL_TABLE()[idx].sg_gui_bg = 0;
  4652.     vim_free(HL_TABLE()[idx].sg_gui_bg_name);
  4653.     HL_TABLE()[idx].sg_gui_bg_name = NULL;
  4654.     HL_TABLE()[idx].sg_font = 0;
  4655.     vim_free(HL_TABLE()[idx].sg_font_name);
  4656.     HL_TABLE()[idx].sg_font_name = NULL;
  4657.     HL_TABLE()[idx].sg_gui_attr = 0;
  4658. #endif
  4659. }
  4660.  
  4661. #ifdef USE_GUI
  4662. /*
  4663.  * Set the normal foreground and background colors according to the "Normal"
  4664.  * highlighighting group.  For X11 also set "Menu" and "Scrollbar" colors.
  4665.  */
  4666.     void
  4667. set_normal_colors()
  4668. {
  4669.     if (set_group_colors((char_u *)"Normal",
  4670.                         &gui.norm_pixel, &gui.back_pixel))
  4671.     {
  4672.     gui_mch_new_colors();
  4673.     must_redraw = CLEAR;
  4674.     }
  4675. #ifdef USE_GUI_X11
  4676.     if (set_group_colors((char_u *)"Menu",
  4677.                       &gui.menu_fg_pixel, &gui.menu_bg_pixel))
  4678.     {
  4679.     gui_mch_new_menu_colors();
  4680.     must_redraw = CLEAR;
  4681.     }
  4682.     if (set_group_colors((char_u *)"Scrollbar",
  4683.                   &gui.scroll_fg_pixel, &gui.scroll_bg_pixel))
  4684.     {
  4685.     gui_new_scrollbar_colors();
  4686.     must_redraw = CLEAR;
  4687.     }
  4688. #endif
  4689. }
  4690.  
  4691. /*
  4692.  * Set the colors for "Normal", "Menu" or "Scrollbar".
  4693.  */
  4694.     static int
  4695. set_group_colors(name, fgp, bgp)
  4696.     char_u    *name;
  4697.     GuiColor    *fgp;
  4698.     GuiColor    *bgp;
  4699. {
  4700.     int        idx;
  4701.  
  4702.     idx = syn_name2id(name) - 1;
  4703.     if (idx >= 0)
  4704.     {
  4705.     gui_do_one_color(idx);
  4706.  
  4707.     if (HL_TABLE()[idx].sg_gui_fg > 0)
  4708.         *fgp = HL_TABLE()[idx].sg_gui_fg - 1;
  4709.     else
  4710.         *fgp = gui.def_norm_pixel;
  4711.     if (HL_TABLE()[idx].sg_gui_bg > 0)
  4712.         *bgp = HL_TABLE()[idx].sg_gui_bg - 1;
  4713.     else
  4714.         *bgp = gui.def_back_pixel;
  4715.     return TRUE;
  4716.     }
  4717.     return FALSE;
  4718. }
  4719.  
  4720. /*
  4721.  * Set font for "Normal" group.  Called by gui_mch_init_font() when a font has
  4722.  * actually chosen to be used.
  4723.  */
  4724.     void
  4725. hl_set_font_name(font_name)
  4726.     char_u    *font_name;
  4727. {
  4728.     int        id;
  4729.  
  4730.     id = syn_name2id((char_u *)"Normal");
  4731.     if (id > 0)
  4732.     {
  4733.     vim_free(HL_TABLE()[id - 1].sg_font_name);
  4734.     HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
  4735.     }
  4736. }
  4737.  
  4738. /*
  4739.  * Set background color for "Normal" group.  Called by gui_mch_init()
  4740.  * when the color is known.
  4741.  */
  4742.     void
  4743. hl_set_bg_color_name(name)
  4744.     char_u  *name;        /* must have been allocated */
  4745. {
  4746.     int        id;
  4747.  
  4748.     if (name != NULL)
  4749.     {
  4750.     id = syn_name2id((char_u *)"Normal");
  4751.     if (id > 0)
  4752.     {
  4753.         vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
  4754.         HL_TABLE()[id - 1].sg_gui_bg_name = name;
  4755.     }
  4756.     }
  4757. }
  4758.  
  4759. /*
  4760.  * Set foreground color for "Normal" group.  Called by gui_mch_init()
  4761.  * when the color is known.
  4762.  */
  4763.     void
  4764. hl_set_fg_color_name(name)
  4765.     char_u  *name;        /* must have been allocated */
  4766. {
  4767.     int        id;
  4768.  
  4769.     if (name != NULL)
  4770.     {
  4771.     id = syn_name2id((char_u *)"Normal");
  4772.     if (id > 0)
  4773.     {
  4774.         vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
  4775.         HL_TABLE()[id - 1].sg_gui_fg_name = name;
  4776.     }
  4777.     }
  4778. }
  4779.  
  4780. /*
  4781.  * Return the handle for a color name.
  4782.  * Returns -1 when failed.
  4783.  */
  4784.     static GuiColor
  4785. color_name2handle(name)
  4786.     char_u  *name;
  4787. {
  4788.     if (STRCMP(name, "NONE") == 0)
  4789.     return (GuiColor)-1;
  4790.  
  4791.     if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
  4792.     return gui.norm_pixel;
  4793.     if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
  4794.     return gui.back_pixel;
  4795.  
  4796.     return gui_mch_get_color(name);
  4797. }
  4798.  
  4799. /*
  4800.  * Return the handle for a font name.
  4801.  * Returns 0 when failed.
  4802.  */
  4803.     static GuiFont
  4804. font_name2handle(name)
  4805.     char_u  *name;
  4806. {
  4807.     if (STRCMP(name, "NONE") == 0)
  4808.     return (GuiFont)0;
  4809.  
  4810.     return gui_mch_get_font(name, TRUE);
  4811. }
  4812. #endif /* USE_GUI */
  4813.  
  4814. /*
  4815.  * Table with the specifications for an attribute number.
  4816.  * Note that this table is used by ALL buffers.  This is required because the
  4817.  * GUI can redraw at any time for any buffer.
  4818.  */
  4819. struct growarray    term_attr_table = {0, 0, 0, 0, NULL};
  4820.  
  4821. #define TERM_ATTR_ENTRY(idx) ((struct attr_entry *)term_attr_table.ga_data)[idx]
  4822.  
  4823. struct growarray    cterm_attr_table = {0, 0, 0, 0, NULL};
  4824.  
  4825. #define CTERM_ATTR_ENTRY(idx) ((struct attr_entry *)cterm_attr_table.ga_data)[idx]
  4826.  
  4827. #ifdef USE_GUI
  4828. struct growarray    gui_attr_table = {0, 0, 0, 0, NULL};
  4829.  
  4830. #define GUI_ATTR_ENTRY(idx) ((struct attr_entry *)gui_attr_table.ga_data)[idx]
  4831. #endif
  4832.  
  4833. /*
  4834.  * Return the attr number for a set of colors and font.
  4835.  * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
  4836.  * if the combination is new.
  4837.  * Return 0 for error (no more room).
  4838.  */
  4839.     static int
  4840. get_attr_entry(table, aep)
  4841.     struct growarray    *table;
  4842.     struct attr_entry    *aep;
  4843. {
  4844.     int            i;
  4845.     struct attr_entry    *gap;
  4846.     static int        recursive = FALSE;
  4847.  
  4848.     /*
  4849.      * Init the table, in case it wasn't done yet.
  4850.      */
  4851.     table->ga_itemsize = sizeof(struct attr_entry);
  4852.     table->ga_growsize = 7;
  4853.  
  4854.     /*
  4855.      * Try to find an entry with the same specifications.
  4856.      */
  4857.     for (i = 0; i < table->ga_len; ++i)
  4858.     {
  4859.     gap = &(((struct attr_entry *)table->ga_data)[i]);
  4860.     if (       aep->ae_attr == gap->ae_attr
  4861.         && (
  4862. #ifdef USE_GUI
  4863.                (table == &gui_attr_table
  4864.             && (aep->ae_u.gui.fg_color == gap->ae_u.gui.fg_color
  4865.                 && aep->ae_u.gui.bg_color == gap->ae_u.gui.bg_color
  4866.                 && aep->ae_u.gui.font == gap->ae_u.gui.font))
  4867.             ||
  4868. #endif
  4869.                (table == &term_attr_table
  4870.             && (aep->ae_u.term.start == NULL) ==
  4871.                         (gap->ae_u.term.start == NULL)
  4872.             && (aep->ae_u.term.start == NULL
  4873.                 || STRCMP(aep->ae_u.term.start,
  4874.                            gap->ae_u.term.start) == 0)
  4875.             && (aep->ae_u.term.stop == NULL) ==
  4876.                          (gap->ae_u.term.stop == NULL)
  4877.             && (aep->ae_u.term.stop == NULL
  4878.                 || STRCMP(aep->ae_u.term.stop,
  4879.                            gap->ae_u.term.stop) == 0))
  4880.             || (table == &cterm_attr_table
  4881.             && aep->ae_u.cterm.fg_color == gap->ae_u.cterm.fg_color
  4882.             && aep->ae_u.cterm.bg_color == gap->ae_u.cterm.bg_color)
  4883.              ))
  4884.  
  4885.     return i + ATTR_OFF;
  4886.     }
  4887.  
  4888.     if (table->ga_len + ATTR_OFF == 256)
  4889.     {
  4890.     /*
  4891.      * Running out of attribute entries!  remove all attributes, and
  4892.      * compute new ones for all groups.
  4893.      * When called recursively, we are really out of numbers.
  4894.      */
  4895.     if (recursive)
  4896.     {
  4897.         EMSG("Too many different highlighting attributes in use");
  4898.         return 0;
  4899.     }
  4900.     recursive = TRUE;
  4901.  
  4902. #ifdef USE_GUI
  4903.     ga_clear(&gui_attr_table);
  4904. #endif
  4905.     ga_clear(&term_attr_table);
  4906.     ga_clear(&cterm_attr_table);
  4907.     must_redraw = CLEAR;
  4908.  
  4909.     for (i = 0; i < highlight_ga.ga_len; ++i)
  4910.         set_hl_attr(i);
  4911.  
  4912.     recursive = FALSE;
  4913.     }
  4914.  
  4915.     /*
  4916.      * This is a new combination of colors and font, add an entry.
  4917.      */
  4918.     if (ga_grow(table, 1) == FAIL)
  4919.     return 0;
  4920.  
  4921.     gap = &(((struct attr_entry *)table->ga_data)[table->ga_len]);
  4922.     vim_memset(gap, 0, sizeof(struct attr_entry));
  4923.     gap->ae_attr = aep->ae_attr;
  4924. #ifdef USE_GUI
  4925.     if (table == &gui_attr_table)
  4926.     {
  4927.     gap->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
  4928.     gap->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
  4929.     gap->ae_u.gui.font = aep->ae_u.gui.font;
  4930.     }
  4931. #endif
  4932.     if (table == &term_attr_table)
  4933.     {
  4934.     if (aep->ae_u.term.start == NULL)
  4935.         gap->ae_u.term.start = NULL;
  4936.     else
  4937.         gap->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
  4938.     if (aep->ae_u.term.stop == NULL)
  4939.         gap->ae_u.term.stop = NULL;
  4940.     else
  4941.         gap->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
  4942.     }
  4943.     else if (table == &cterm_attr_table)
  4944.     {
  4945.     gap->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
  4946.     gap->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
  4947.     }
  4948.     ++table->ga_len;
  4949.     --table->ga_room;
  4950.     return (table->ga_len - 1 + ATTR_OFF);
  4951. }
  4952.  
  4953. #ifdef USE_GUI
  4954.  
  4955.     struct attr_entry *
  4956. syn_gui_attr2entry(attr)
  4957.     int            attr;
  4958. {
  4959.     attr -= ATTR_OFF;
  4960.     if (attr >= gui_attr_table.ga_len)        /* did ":syntax clear" */
  4961.     return NULL;
  4962.     return &(GUI_ATTR_ENTRY(attr));
  4963. }
  4964.  
  4965. #endif /* USE_GUI */
  4966.  
  4967.     struct attr_entry *
  4968. syn_term_attr2entry(attr)
  4969.     int            attr;
  4970. {
  4971.     attr -= ATTR_OFF;
  4972.     if (attr >= term_attr_table.ga_len)        /* did ":syntax clear" */
  4973.     return NULL;
  4974.     return &(TERM_ATTR_ENTRY(attr));
  4975. }
  4976.  
  4977.     struct attr_entry *
  4978. syn_cterm_attr2entry(attr)
  4979.     int            attr;
  4980. {
  4981.     attr -= ATTR_OFF;
  4982.     if (attr >= cterm_attr_table.ga_len)    /* did ":syntax clear" */
  4983.     return NULL;
  4984.     return &(CTERM_ATTR_ENTRY(attr));
  4985. }
  4986.  
  4987. #define LIST_ATTR   1
  4988. #define LIST_STRING 2
  4989. #define LIST_INT    3
  4990.  
  4991.     static void
  4992. highlight_list_one(id)
  4993.     int        id;
  4994. {
  4995.     struct hl_group    *sgp;
  4996.     int            didh = FALSE;
  4997.  
  4998.     sgp = &HL_TABLE()[id - 1];        /* index is ID minus one */
  4999.  
  5000.     didh = highlight_list_arg(id, didh, LIST_ATTR,
  5001.                     sgp->sg_term, NULL, "term");
  5002.     didh = highlight_list_arg(id, didh, LIST_STRING,
  5003.                     0, sgp->sg_start, "start");
  5004.     didh = highlight_list_arg(id, didh, LIST_STRING,
  5005.                     0, sgp->sg_stop, "stop");
  5006.  
  5007.     didh = highlight_list_arg(id, didh, LIST_ATTR,
  5008.                     sgp->sg_cterm, NULL, "cterm");
  5009.     didh = highlight_list_arg(id, didh, LIST_INT,
  5010.                     sgp->sg_cterm_fg, NULL, "ctermfg");
  5011.     didh = highlight_list_arg(id, didh, LIST_INT,
  5012.                     sgp->sg_cterm_bg, NULL, "ctermbg");
  5013.  
  5014. #ifdef USE_GUI
  5015.     didh = highlight_list_arg(id, didh, LIST_ATTR,
  5016.                     sgp->sg_gui, NULL, "gui");
  5017.     didh = highlight_list_arg(id, didh, LIST_STRING,
  5018.                     0, sgp->sg_gui_fg_name, "guifg");
  5019.     didh = highlight_list_arg(id, didh, LIST_STRING,
  5020.                     0, sgp->sg_gui_bg_name, "guibg");
  5021.     didh = highlight_list_arg(id, didh, LIST_STRING,
  5022.                     0, sgp->sg_font_name, "font");
  5023. #endif
  5024.  
  5025.     if (sgp->sg_link)
  5026.     {
  5027.     (void)syn_list_header(didh, 9999, id);
  5028.     msg_puts_attr((char_u *)"links to", highlight_attr[HLF_D]);
  5029.     msg_putchar(' ');
  5030.     msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
  5031.     }
  5032. }
  5033.  
  5034.     static int
  5035. highlight_list_arg(id, didh, type, iarg, sarg, name)
  5036.     int        id;
  5037.     int        didh;
  5038.     int        type;
  5039.     int        iarg;
  5040.     char_u    *sarg;
  5041.     char    *name;
  5042. {
  5043.     char_u    buf[100];
  5044.     char_u    *ts;
  5045.     int        i;
  5046.  
  5047.     if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
  5048.     {
  5049.     ts = buf;
  5050.     if (type == LIST_INT)
  5051.         sprintf((char *)buf, "%d", iarg - 1);
  5052.     else if (type == LIST_STRING)
  5053.         ts = sarg;
  5054.     else /* type == LIST_ATTR */
  5055.     {
  5056.         buf[0] = NUL;
  5057.         for (i = 0; hl_attr_table[i] != 0; ++i)
  5058.         {
  5059.         if (iarg & hl_attr_table[i])
  5060.         {
  5061.             if (buf[0] != NUL)
  5062.             STRCAT(buf, ",");
  5063.             STRCAT(buf, hl_name_table[i]);
  5064.             iarg &= ~hl_attr_table[i];        /* don't want "inverse" */
  5065.         }
  5066.         }
  5067.     }
  5068.  
  5069.     (void)syn_list_header(didh,
  5070.                    (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
  5071.     didh = TRUE;
  5072.  
  5073.     MSG_PUTS_ATTR(name, highlight_attr[HLF_D]);
  5074.     MSG_PUTS_ATTR("=", highlight_attr[HLF_D]);
  5075.     msg_outtrans(ts);
  5076.     }
  5077.     return didh;
  5078. }
  5079.  
  5080. /*
  5081.  * Return "1" if highlight group "id" has attribute "flag" for GUI.
  5082.  * Return NULL otherwise.
  5083.  */
  5084.     char_u *
  5085. highlight_has_attr(id, flag)
  5086.     int        id;
  5087.     int        flag;
  5088. {
  5089.     int        attr;
  5090.  
  5091.     if (id <= 0 || id > highlight_ga.ga_len)
  5092.     return NULL;
  5093. #ifdef USE_GUI
  5094.     if (gui.in_use)
  5095.     attr = HL_TABLE()[id - 1].sg_gui;
  5096.     else
  5097. #endif
  5098.      if (*T_CCO)
  5099.     attr = HL_TABLE()[id - 1].sg_cterm;
  5100.     else
  5101.     attr = HL_TABLE()[id - 1].sg_term;
  5102.  
  5103.     if (attr & flag)
  5104.     return (char_u *)"1";
  5105.     return NULL;
  5106. }
  5107.  
  5108. /*
  5109.  * Return color name of highlight group "id".
  5110.  */
  5111.     char_u *
  5112. highlight_color(id, what)
  5113.     int        id;
  5114.     char_u  *what;    /* "fg", "bg", "fg#" or "bg#" */
  5115. {
  5116.     static char_u    name[20];
  5117.     int            n;
  5118.     int            fg;
  5119.  
  5120.     if (id <= 0 || id > highlight_ga.ga_len)
  5121.     return NULL;
  5122.  
  5123.     if (TO_LOWER(what[0]) == 'f')
  5124.     fg = TRUE;
  5125.     else
  5126.     fg = FALSE;
  5127. #ifdef USE_GUI
  5128.     if (gui.in_use)
  5129.     {
  5130.     if (what[1] && what[2] == '#')        /* return #RRGGBB form */
  5131.     {
  5132.         GuiColor    color;
  5133.  
  5134.         if (fg)
  5135.         color = HL_TABLE()[id - 1].sg_gui_fg;
  5136.         else
  5137.         color = HL_TABLE()[id - 1].sg_gui_bg;
  5138.         if (color == 0)
  5139.         return NULL;
  5140.         return gui_mch_get_rgb(color - 1);
  5141.     }
  5142.     if (fg)
  5143.         return (HL_TABLE()[id - 1].sg_gui_fg_name);
  5144.     return (HL_TABLE()[id - 1].sg_gui_bg_name);
  5145.     }
  5146. #endif
  5147.     if (*T_CCO)
  5148.     {
  5149.     if (fg)
  5150.         n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
  5151.     else
  5152.         n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
  5153.     sprintf((char *)name, "%d", n);
  5154.     return name;
  5155.     }
  5156.     return NULL;
  5157. }
  5158.  
  5159. /*
  5160.  * Output the syntax list header.
  5161.  * Return TRUE when started a new line.
  5162.  */
  5163.     static int
  5164. syn_list_header(did_header, outlen, id)
  5165.     int        did_header;        /* did header already */
  5166.     int        outlen;        /* length of string that comes */
  5167.     int        id;            /* highlight group id */
  5168. {
  5169.     int        endcol = 15;
  5170.     int        newline = TRUE;
  5171.  
  5172.     if (!did_header)
  5173.     {
  5174.     msg_putchar('\n');
  5175.     msg_outtrans(HL_TABLE()[id - 1].sg_name);
  5176.     }
  5177.     else if (msg_col + outlen + 1 >= Columns)
  5178.     msg_putchar('\n');
  5179.     else
  5180.     {
  5181.     if (msg_col >= endcol)    /* wrap around is like starting a new line */
  5182.         newline = FALSE;
  5183.     msg_putchar(' ');
  5184.     }
  5185.  
  5186.     if (msg_col >= endcol)    /* output at least one space */
  5187.     endcol = msg_col + 1;
  5188.     if (Columns <= endcol)    /* avoid hang for tiny window */
  5189.     endcol = Columns - 1;
  5190.  
  5191.     msg_advance(endcol);
  5192.  
  5193.     return newline;
  5194. }
  5195.  
  5196. /*
  5197.  * Set the attribute numbers for a highlight group.
  5198.  * Called after one of the attributes has changed.
  5199.  */
  5200.     static void
  5201. set_hl_attr(idx)
  5202.     int            idx;        /* index in array */
  5203. {
  5204.     struct attr_entry at_en;
  5205.  
  5206.     /* The "Normal" group doesn't need an attribute number */
  5207.     if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
  5208.     return;
  5209.  
  5210. #ifdef USE_GUI
  5211.     /*
  5212.      * For the GUI mode: If there are other than "normal" highlighting
  5213.      * attributes, need to allocate an attr number.
  5214.      */
  5215.     if (HL_TABLE()[idx].sg_gui_fg == 0
  5216.         && HL_TABLE()[idx].sg_gui_bg == 0
  5217.         && HL_TABLE()[idx].sg_font == 0)
  5218.     {
  5219.     HL_TABLE()[idx].sg_gui_attr = HL_TABLE()[idx].sg_gui;
  5220.     }
  5221.     else
  5222.     {
  5223.     at_en.ae_attr = HL_TABLE()[idx].sg_gui;
  5224.     at_en.ae_u.gui.fg_color = HL_TABLE()[idx].sg_gui_fg;
  5225.     at_en.ae_u.gui.bg_color = HL_TABLE()[idx].sg_gui_bg;
  5226.     at_en.ae_u.gui.font = HL_TABLE()[idx].sg_font;
  5227.     HL_TABLE()[idx].sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
  5228.     }
  5229. #endif
  5230.     /*
  5231.      * For the term mode: If there are other than "normal" highlighting
  5232.      * attributes, need to allocate an attr number.
  5233.      */
  5234.     if (HL_TABLE()[idx].sg_start == NULL && HL_TABLE()[idx].sg_stop == NULL)
  5235.     HL_TABLE()[idx].sg_term_attr = HL_TABLE()[idx].sg_term;
  5236.     else
  5237.     {
  5238.     at_en.ae_attr = HL_TABLE()[idx].sg_term;
  5239.     at_en.ae_u.term.start = HL_TABLE()[idx].sg_start;
  5240.     at_en.ae_u.term.stop = HL_TABLE()[idx].sg_stop;
  5241.     HL_TABLE()[idx].sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
  5242.     }
  5243.  
  5244.     /*
  5245.      * For the color term mode: If there are other than "normal"
  5246.      * highlighting attributes, need to allocate an attr number.
  5247.      */
  5248.     if (HL_TABLE()[idx].sg_cterm_fg == 0 && HL_TABLE()[idx].sg_cterm_bg == 0)
  5249.     HL_TABLE()[idx].sg_cterm_attr = HL_TABLE()[idx].sg_cterm;
  5250.     else
  5251.     {
  5252.     at_en.ae_attr = HL_TABLE()[idx].sg_cterm;
  5253.     at_en.ae_u.cterm.fg_color = HL_TABLE()[idx].sg_cterm_fg;
  5254.     at_en.ae_u.cterm.bg_color = HL_TABLE()[idx].sg_cterm_bg;
  5255.     HL_TABLE()[idx].sg_cterm_attr =
  5256.                     get_attr_entry(&cterm_attr_table, &at_en);
  5257.     }
  5258. }
  5259.  
  5260. /*
  5261.  * Lookup a highlight group name and return it's ID.
  5262.  * If it is not found, 0 is returned.
  5263.  */
  5264.     int
  5265. syn_name2id(name)
  5266.     char_u    *name;
  5267. {
  5268.     int        i;
  5269.     char_u    *name_u;
  5270.  
  5271.     /* Avoid using stricmp() too much, it's slow on some systems */
  5272.     name_u = vim_strsave_up(name);
  5273.     if (name_u == NULL)
  5274.     return 0;
  5275.     for (i = highlight_ga.ga_len; --i >= 0; )
  5276.     if (HL_TABLE()[i].sg_name_u != NULL
  5277.         && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
  5278.         break;
  5279.     vim_free(name_u);
  5280.     return i + 1;
  5281. }
  5282.  
  5283. /*
  5284.  * Return TRUE if highlight group "name" exists.
  5285.  */
  5286.     int
  5287. highlight_exists(name)
  5288.     char_u    *name;
  5289. {
  5290.     return (syn_name2id(name) > 0);
  5291. }
  5292.  
  5293. /*
  5294.  * Like syn_name2id(), but take a pointer + length argument.
  5295.  */
  5296.     static int
  5297. syn_namen2id(linep, len)
  5298.     char_u  *linep;
  5299.     int        len;
  5300. {
  5301.     char_u  *name;
  5302.     int        id = 0;
  5303.  
  5304.     name = vim_strnsave(linep, len);
  5305.     if (name != NULL)
  5306.     {
  5307.     id = syn_name2id(name);
  5308.     vim_free(name);
  5309.     }
  5310.     return id;
  5311. }
  5312.  
  5313. /*
  5314.  * Find highlight group name in the table and return it's ID.
  5315.  * The argument is a pointer to the name and the length of the name.
  5316.  * If it doesn't exist yet, a new entry is created.
  5317.  * Return 0 for failure.
  5318.  */
  5319.     int
  5320. syn_check_group(pp, len)
  5321.     char_u        *pp;
  5322.     int            len;
  5323. {
  5324.     int        id;
  5325.     char_u  *name;
  5326.  
  5327.     name = vim_strnsave(pp, len);
  5328.     if (name == NULL)
  5329.     return 0;
  5330.  
  5331.     id = syn_name2id(name);
  5332.     if (id == 0)            /* doesn't exist yet */
  5333.     id = syn_add_group(name);
  5334.     else
  5335.     vim_free(name);
  5336.     return id;
  5337. }
  5338.  
  5339. /*
  5340.  * Add new highlight group and return it's ID.
  5341.  * "name" must be an allocated string, it will be consumed.
  5342.  * Return 0 for failure.
  5343.  */
  5344.     static int
  5345. syn_add_group(name)
  5346.     char_u        *name;
  5347. {
  5348.     /*
  5349.      * First call for this growarray: init growing array.
  5350.      */
  5351.     if (highlight_ga.ga_data == NULL)
  5352.     {
  5353.     highlight_ga.ga_itemsize = sizeof(struct hl_group);
  5354.     highlight_ga.ga_growsize = 10;
  5355.     }
  5356.  
  5357.     /*
  5358.      * Make room for at least one other syntax_highlight entry.
  5359.      */
  5360.     if (ga_grow(&highlight_ga, 1) == FAIL)
  5361.     {
  5362.     vim_free(name);
  5363.     return 0;
  5364.     }
  5365.  
  5366.     vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
  5367.     HL_TABLE()[highlight_ga.ga_len].sg_name = name;
  5368.     HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
  5369.     ++highlight_ga.ga_len;
  5370.     --highlight_ga.ga_room;
  5371.  
  5372.     return highlight_ga.ga_len;            /* ID is index plus one */
  5373. }
  5374.  
  5375. /*
  5376.  * When, just after calling syn_add_group(), an error is discovered, this
  5377.  * function deletes the new name.
  5378.  */
  5379.     static void
  5380. syn_unadd_group()
  5381. {
  5382.     --highlight_ga.ga_len;
  5383.     ++highlight_ga.ga_room;
  5384.     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
  5385.     vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
  5386. }
  5387.  
  5388. /*
  5389.  * Translate a group ID to highlight attributes.
  5390.  */
  5391.     int
  5392. syn_id2attr(hl_id)
  5393.     int            hl_id;
  5394. {
  5395.     int            attr;
  5396.     struct hl_group    *sgp;
  5397.  
  5398.     hl_id = syn_get_final_id(hl_id);
  5399.     sgp = &HL_TABLE()[hl_id - 1];        /* index is ID minus one */
  5400.  
  5401. #ifdef USE_GUI
  5402.     /*
  5403.      * Only use GUI attr when the GUI is being used.
  5404.      */
  5405.     if (gui.in_use)
  5406.     attr = sgp->sg_gui_attr;
  5407.     else
  5408. #endif
  5409.     if (*T_CCO)
  5410.         attr = sgp->sg_cterm_attr;
  5411.     else
  5412.         attr = sgp->sg_term_attr;
  5413.  
  5414.     return attr;
  5415. }
  5416.  
  5417. #ifdef USE_GUI
  5418. /*
  5419.  * Get the GUI colors and attributes for a group ID.
  5420.  * NOTE: the colors will be 0 when not set, the color plus one otherwise.
  5421.  */
  5422.     int
  5423. syn_id2colors(hl_id, fgp, bgp)
  5424.     int        hl_id;
  5425.     GuiColor    *fgp;
  5426.     GuiColor    *bgp;
  5427. {
  5428.     struct hl_group    *sgp;
  5429.  
  5430.     hl_id = syn_get_final_id(hl_id);
  5431.     sgp = &HL_TABLE()[hl_id - 1];        /* index is ID minus one */
  5432.  
  5433.     *fgp = sgp->sg_gui_fg;
  5434.     *bgp = sgp->sg_gui_bg;
  5435.     return sgp->sg_gui;
  5436. }
  5437. #endif
  5438.  
  5439. /*
  5440.  * Translate a group ID to the final group ID (following links).
  5441.  */
  5442.     int
  5443. syn_get_final_id(hl_id)
  5444.     int            hl_id;
  5445. {
  5446.     int            count;
  5447.     struct hl_group    *sgp;
  5448.  
  5449.     /*
  5450.      * Follow links until there is no more.
  5451.      * Look out for loops!  Break after 100 links.
  5452.      */
  5453.     for (count = 100; --count >= 0; )
  5454.     {
  5455.     sgp = &HL_TABLE()[hl_id - 1];        /* index is ID minus one */
  5456.     if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
  5457.         break;
  5458.     hl_id = sgp->sg_link;
  5459.     }
  5460.  
  5461.     return hl_id;
  5462. }
  5463.  
  5464. #ifdef USE_GUI
  5465. /*
  5466.  * Call this function just after the GUI has started.
  5467.  * It finds the font and color handles for the highlighting groups.
  5468.  */
  5469.     void
  5470. highlight_gui_started()
  5471. {
  5472.     int        idx;
  5473.  
  5474.     /* First get the colors from the "Normal" and "Menu" group, if set */
  5475.     set_normal_colors();
  5476.  
  5477.     for (idx = 0; idx < highlight_ga.ga_len; ++idx)
  5478.     gui_do_one_color(idx);
  5479.  
  5480.     highlight_changed();
  5481. }
  5482.  
  5483.     static void
  5484. gui_do_one_color(idx)
  5485.     int        idx;
  5486. {
  5487.     int        didit = FALSE;
  5488.  
  5489.     if (HL_TABLE()[idx].sg_font_name != NULL)
  5490.     {
  5491.     HL_TABLE()[idx].sg_font =
  5492.         font_name2handle(HL_TABLE()[idx].sg_font_name);
  5493.     didit = TRUE;
  5494.     }
  5495.     if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
  5496.     {
  5497.     HL_TABLE()[idx].sg_gui_fg =
  5498.         color_name2handle(HL_TABLE()[idx].sg_gui_fg_name) + 1;
  5499.     didit = TRUE;
  5500.     }
  5501.     if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
  5502.     {
  5503.     HL_TABLE()[idx].sg_gui_bg =
  5504.         color_name2handle(HL_TABLE()[idx].sg_gui_bg_name) + 1;
  5505.     didit = TRUE;
  5506.     }
  5507.     if (didit)    /* need to get a new attr number */
  5508.     set_hl_attr(idx);
  5509. }
  5510.  
  5511. #endif
  5512.  
  5513. /*
  5514.  * Translate the 'highlight' option into attributes in highlight_attr[].
  5515.  * Called only when the 'highlight' option has been changed.
  5516.  * Return FAIL when an invalid flag is found.  OK otherwise.
  5517.  */
  5518.     int
  5519. highlight_changed()
  5520. {
  5521.     int        hlf;
  5522.     int        i;
  5523.     char_u    *p;
  5524.     int        attr;
  5525.     char_u    *end;
  5526.     int        id;
  5527.  
  5528.     /* Check the HLF_ enums, they must be in the same order! */
  5529.     static int flags[HLF_COUNT] = {'8', '@', 'd', 'e', 'h', 'i', 'l', 'm', 'M',
  5530.                    'n', 'r', 's', 'S', 't', 'v', 'w'};
  5531.  
  5532.     need_highlight_changed = FALSE;
  5533.  
  5534.     /*
  5535.      * Clear all attributes.
  5536.      */
  5537.     for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
  5538.     highlight_attr[hlf] = 0;
  5539.  
  5540.     /*
  5541.      * First set all attributes to their default value.
  5542.      * Then use the attributes from the 'highlight' option.
  5543.      */
  5544.     for (i = 0; i < 2; ++i)
  5545.     {
  5546.     if (i)
  5547.         p = p_hl;
  5548.     else
  5549.         p = get_highlight_default();
  5550.     if (p == NULL)        /* just in case */
  5551.         continue;
  5552.  
  5553.     while (*p)
  5554.     {
  5555.         for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
  5556.         if (flags[hlf] == *p)
  5557.             break;
  5558.         ++p;
  5559.         if (hlf == (int)HLF_COUNT || *p == NUL)
  5560.         return FAIL;
  5561.  
  5562.         /*
  5563.          * Allow several flags to be combined, like "bu" for
  5564.          * bold-underlined.
  5565.          */
  5566.         attr = 0;
  5567.         for ( ; *p && *p != ','; ++p)        /* parse upto comma */
  5568.         {
  5569.         if (vim_iswhite(*p))            /* ignore white space */
  5570.             continue;
  5571.  
  5572.         if (attr > HL_ALL)  /* Combination with ':' is not allowed. */
  5573.             return FAIL;
  5574.  
  5575.         switch (*p)
  5576.         {
  5577.             case 'b':    attr |= HL_BOLD;
  5578.                 break;
  5579.             case 'i':    attr |= HL_ITALIC;
  5580.                 break;
  5581.             case '-':
  5582.             case 'n':                /* no highlighting */
  5583.                 break;
  5584.             case 'r':    attr |= HL_INVERSE;
  5585.                 break;
  5586.             case 's':    attr |= HL_STANDOUT;
  5587.                 break;
  5588.             case 'u':    attr |= HL_UNDERLINE;
  5589.                 break;
  5590.             case ':':    ++p;            /* highlight group name */
  5591.                 if (attr || *p == NUL)     /* no combinations */
  5592.                     return FAIL;
  5593.                 end = vim_strchr(p, ',');
  5594.                 if (end == NULL)
  5595.                     end = p + STRLEN(p);
  5596.                 id = syn_check_group(p, (int)(end - p));
  5597.                 if (id == 0)
  5598.                     return FAIL;
  5599.                 attr = syn_id2attr(id);
  5600.                 p = end - 1;
  5601.                 break;
  5602.             default:    return FAIL;
  5603.         }
  5604.         }
  5605.         highlight_attr[hlf] = attr;
  5606.  
  5607.         p = skip_to_option_part(p);        /* skip comma and spaces */
  5608.     }
  5609.     }
  5610.  
  5611.     return OK;
  5612. }
  5613.  
  5614. /*
  5615.  * Handle command line completion for :highlight command.
  5616.  */
  5617.     void
  5618. set_context_in_highlight_cmd(arg)
  5619.     char_u *arg;
  5620. {
  5621.     char_u    *p;
  5622.  
  5623.     /* Default: expand group names */
  5624.     expand_context = EXPAND_HIGHLIGHT;
  5625.     expand_pattern = arg;
  5626.     include_link = TRUE;
  5627.  
  5628.     /* (part of) subcommand already typed */
  5629.     if (*arg != NUL)
  5630.     {
  5631.     p = skiptowhite(arg);
  5632.     if (*p != NUL)            /* past group name */
  5633.     {
  5634.         include_link = FALSE;
  5635.         if (STRNCMP("link", arg, p - arg) == 0
  5636.             || STRNCMP("clear", arg, p - arg) == 0)
  5637.         {
  5638.         expand_pattern = skipwhite(p);
  5639.         p = skiptowhite(expand_pattern);
  5640.         if (*p != NUL)            /* past first group name */
  5641.         {
  5642.             expand_pattern = skipwhite(p);
  5643.             p = skiptowhite(expand_pattern);
  5644.         }
  5645.         }
  5646.         if (*p != NUL)            /* past group name(s) */
  5647.         expand_context = EXPAND_NOTHING;
  5648.     }
  5649.     }
  5650. }
  5651.  
  5652. /*
  5653.  * Function given to ExpandGeneric() to obtain the list of group names.
  5654.  * Also used for synIDattr() function.
  5655.  */
  5656.     char_u *
  5657. get_highlight_name(idx)
  5658.     int        idx;
  5659. {
  5660.     if (idx == highlight_ga.ga_len && include_link)
  5661.     return (char_u *)"link";
  5662.     if (idx == highlight_ga.ga_len + 1 && include_link)
  5663.     return (char_u *)"clear";
  5664.     if (idx < 0 || idx >= highlight_ga.ga_len)
  5665.     return NULL;
  5666.     return HL_TABLE()[idx].sg_name;
  5667. }
  5668.  
  5669. /**************************************
  5670.  *  End of Highlighting stuff          *
  5671.  **************************************/
  5672.